正则表达式入门

正则表达式(Regular Expression)是一种文本模式,包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为"元字符")。

正则表达式使用单个字符串来描述、匹配一系列匹配某个句法规则的字符串。

 

一、基本语法

在JS中,正则表达式为对象,用如下两种方式定义:

直接量法: /pattern/attributes;创建RegExp对象法:new RegExp(pattern,attributes);

var reg=/hi/i;//字面量

var reg=new RegExp('hi','i');//构造函数(参数1:最简单的正则匹配字母hi;参数2:表示匹配时不分大小写)

 

 1、直接量

  最简单的正则匹配模式,使用字符本身进行匹配

var reg=/hi/;//匹配一个hi字符

 

 2、元字符

元字符是一些符号和字母的组合,用来替代普通字符,元字符有.\d\w\s等

代码

说明

.

匹配除换行符以外的任意字符

\w

匹配字母或数字或下划线或汉字

\s

匹配任意的空白符

\d

匹配数字

\b

匹配单词的开始或结束

^

匹配字符串的开始

$

匹配字符串的结束

 

var reg=/./;// .表示除换行符以外的任一字符

var reg=/\d/;// \d表示0-9的数字

 

3、字符转义

  如果需要查找元字符本身,则需要在元字符之前加\进行转义

var reg=/\./;//匹配一个.

var reg=/\\/;// 匹配一个\

 

4重复

 量词用于限定它前面表达式匹配的次数,有*+?{}等

var reg=/\d\d\d/;//匹配3个数字

var reg=/\d*/;//匹配任意数量数字,可能是0个

var reg=/\d+/;//匹配1个到多个数字

var reg=/\d?/;//匹配0到1个数字

var reg=/\d{3}/;//匹配3个数字

var reg=/\d{3,}/;//匹配3到多个数字

var reg=/\d{3,6}/;//匹配3到6个数字

 

5、字符类

  在[]的范围中添加字符,在中括号内的元字符不需要进行转义‘-’表示至,它的作用为在括号中任选其一

var reg=/[abc]/;//匹配'a','b','c'其中之一

var reg=/[() +\]/;//匹配'(',')',' ','+','\'其中之一,[]内部不需要转义

var reg=/[0-9a-z]/;//匹配0至9和a至z,即[0123456789abcdefghijklmnopqrstuvwxyz]

var reg=/[0-9][a-z]/;//匹配前一个数字后一个字母的两位字符,区别于上述匹配一位字符

 

6、分支条件

  使用|把不同规则分隔开,类似条件选择,从左到右依次测试每个条件,满足其中任一条件即可,当满足某个分枝成功后就不再管其它的分支了

var reg=/0\d{2}-\d{8}|0\d{3}-\d{7}/;//匹配固定电话:前3后8或前4后7,即021-12345678或0553-7654321

 

7、分组

  使用()来包裹子表达式,以便对这部分子表达式整体操作,如重复或后向引用

var reg=/((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)/;//匹配IP地址

//意思为:((一个不大于255的数字)+‘.’)整体重复三次+(一个不大于255的数字)

 

8、反义

  查找不属于指定类型以外的字符,有\D\W\S[^]等,其中\D等价于[^\d] 除数字以外任意字符

var reg=/[^abc]/;//匹配一个不是abc的字符,即除abc以外的任一字符

 

 9、后向引用

  使用()分组匹配的子表达式,将自动拥有一个分组编号,可用于后续重复引用

var reg=/((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}\2/;//这里有分组有嵌套,\1代表外层分组(下划线),\2表示内层分组(红色)

  可以自定义分组编号(即命名)语法为:(?<name>exp)或(?'name'exp),将分组命名为name,再反向引用时语法为:\k<name>

 

var reg=/((?<name>2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}\k<name>/;//给分组2命名为name,后续可用该名称来引用

  还可以忽略当前分组获得自动编号(即隐藏分组),语法为(?:exp)

 

var reg=/(?:(2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}\1/;//因为第一个分组被忽略,原来的分组2变为了分组1

 

 10、零宽度断言(也称环视)

  用于查找在某些内容(但并不包括这些内容)之前或之后的东西,类似\b^$指定一个位置,这个位置要满足一定的条件(断言)

 

  (?=exp)正前瞻,自身出现的位置的后面能匹配表达式exp,如:\b\w+(?=ing\b),匹配以ing结尾的单词的前面部分,等同于下划线部分

 

  (?!exp)反前瞻,自身出现的位置的后面不能匹配表达式exp,如:\babc(?!ing\b),匹配不以ing结尾的abc开头单词的abc,等同于下划线部分

 

  (?<=exp)正后顾,自身出现的位置的前面能匹配表达式exp,如:(?<=\bre)\w+\b,匹配以re开头的单词的后半部分,等同于下划线部分(JS不支持)

 

  (?<!exp)反后顾,自身出现的位置的前面不能匹配表达式exp,如:(?<!\bre)abc\b,匹配以re开头的abc结尾单词的abc,等同于下划线部分(JS不支持)

 

var str='reading';

var reg=/read(?=ing)/g;//正前瞻:在我们捕获read这个字符串时,筛选它接下来的字符串是不是ing

console.log(str.match(reg)[0]);//read

 

11贪婪与懒惰

  当正则表达式中包含能接受重复的限定符时,通常的行为是(在使整个表达式能得到匹配的前提下)匹配尽可能多的字符。以这个表达式为例:a.*b,它将会匹配最长的以a开始,以b结束的字符串。如果用它来搜索aabab的话,它会匹配整个字符串aabab。这被称为贪婪匹配。

有时,我们更需要懒惰匹配,也就是匹配尽可能少的字符。前面给出的限定符都可以被转化为懒惰匹配模式,只要在它后面加上一个问号?。这样.*?就意味着匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复。现在看看懒惰版的例子吧:

a.*?b匹配最短的,以a开始,以b结束的字符串。如果把它应用于aabab的话,它会匹配aab(第一到第三个字符)和ab(第四到第五个字符)。

代码/语法

说明

*?

重复任意次,但尽可能少重复

+?

重复1次或更多次,但尽可能少重复

??

重复0次或1次,但尽可能少重复

{n,m}?

重复n到m次,但尽可能少重复

{n,}?

重复n次以上,但尽可能少重复

 

 12处理选项

 几个选项如忽略大小写,处理多行等,这些选项能用来改变处理正则表达式的方式。

 

名称

说明

IgnoreCase(忽略大小写)

匹配时不区分大小写。

Multiline(多行模式)

更改^和$的含义,使它们分别在任意一行的行首和行尾匹配,而不仅仅在整个字符串的开头和结尾匹配。(在此模式下,$的精确含意是:匹配\n之前的位置以及字符串结束前的位置.)

Singleline(单行模式)

更改.的含义,使它与每一个字符匹配(包括换行符\n)。

IgnorePatternWhitespace(忽略空白)

忽略表达式中的非转义空白并启用由#标记的注释。

ExplicitCapture(显式捕获)

仅捕获已被显式命名的组。

一个经常被问到的问题是:是不是只能同时使用多行模式和单行模式中的一种?答案是:不是。这两个选项之间没有任何关系,除了它们的名字比较相似(以至于让人感到疑惑)以外。

 

13平衡组/递归匹配

有时我们需要匹配像( 100 * ( 50 + 15 ) )这样的可嵌套的层次性结构,这时简单地使用\(.+\)则只会匹配到最左边的左括号和最右边的右括号之间的内容(这里我们讨论的是贪婪模式,懒惰模式也有下面的问题)。假如原来的字符串里的左括号和右括号出现的次数不相等,比如( 5 / ( 3 + 2 ) ) ),那我们的匹配结果里两者的个数也不会相等。有没有办法在这样的字符串里匹配到最长的,配对的括号之间的内容呢?

为了避免(和\(把你的大脑彻底搞糊涂,我们还是用尖括号代替圆括号吧。现在我们的问题变成了如何把xx <aa <bbb> <bbb> aa> yy这样的字符串里,最长的配对的尖括号内的内容捕获出来?

这里需要用到以下的语法构造:

(?'group') 把捕获的内容命名为group,并压入堆栈(Stack)

(?'-group') 从堆栈上弹出最后压入堆栈的名为group的捕获内容,如果堆栈本来为空,则本分组的匹配失败

(?(group)yes|no) 如果堆栈上存在以名为group的捕获内容的话,继续匹配yes部分的表达式,否则继续匹配no部分

(?!) 零宽负向先行断言,由于没有后缀表达式,试图匹配总是失败

我们需要做的是每碰到了左括号,就在压入一个"Open",每碰到一个右括号,就弹出一个,到了最后就看看堆栈是否为空--如果不为空那就证明左括号比右括号多,那匹配就应该失败。正则表达式引擎会进行回溯(放弃最前面或最后面的一些字符),尽量使整个表达式得到匹配。

<                         #最外层的左括号

    [^<>]*                #最外层的左括号后面的不是括号的内容

    (

        (

            (?'Open'<)    #碰到了左括号,在黑板上写一个"Open"

            [^<>]*       #匹配左括号后面的不是括号的内容

        )+

        (

            (?'-Open'>)   #碰到了右括号,擦掉一个"Open"

            [^<>]*        #匹配右括号后面不是括号的内容

        )+

    )*

    (?(Open)(?!))         #在遇到最外层的右括号前面,判断黑板上还有没有没擦掉的"Open";如果还有,则匹配失败

>                         #最外层的右括号

平衡组的一个最常见的应用就是匹配HTML,下面这个例子可以匹配嵌套的<div>标签:<div[^>]*>[^<>]*(((?'Open'<div[^>]*>)[^<>]*)+((?'-Open'</div>)[^<>]*)+)*(?(Open)(?!))</div>.

如果你不是一个程序员(或者你自称程序员但是不知道堆栈是什么东西),你就这样理解上面的三种语法吧:第一个就是在黑板上写一个"group",第二个就是从黑板上擦掉一个"group",第三个就是看黑板上写的还有没有"group",如果有就继续匹配yes部分,否则就匹配no部分。

 

附表:常用元字符详解

元字符

类型

说明

\

特殊

转义符号,或将下一个字符标记为特殊字符(如\d)

|

特殊

分支条件,类似条件选择把不同规则分隔开

()

特殊

分组

[]

特殊

限定字符,如[abc]

[^]

特殊

负值字符串,如[^abc]除abc以外的任何字符

{}

特殊

限定次数

^

位置

匹配字符串的开始

$

位置

匹配字符串的结束

\b

位置

单词的开头或结尾,也就是单词的分界处

*

量词

重复零次或更多次(任意次)

+

量词

重复一次或更多次(至少出现一次)

量词

重复零次或一次(最多出现一次)

{n}

量词

重复n次,n为数字

{n,}

量词

重复n次到无数次

{n,m}

量词

重复n次到m次

.

匹配

匹配除换行符以外的任意字符

\d

匹配

匹配一个数字字符,等价于[0-9]

\w

匹配

匹配字母或数字或下划线或汉字,只考虑英文情况下等价于[a-zA-Z0-9_]

\s

匹配

匹配任意的空白符

 

 二、RegExp对象的方法

1、test()

  基本语法:RegExpObject.test(str);

  该方法用于检测一个字符串是否匹配某个模式(即给定的字符串使用该正则是否能匹配到内容),返回一个布尔值

var str="hello world";

var regexp=new RegExp("hello");//构造函数创建RegExp对象

console.log(regexp.test(str));//true

console.log(/hi/.test(str));//false,//字面量

 

2、exec()

  基本语法:RegExpObject.exec(str);

  该方法用于检索字符串中的正则表达式的匹配,返回一个匹配结果的数组,如果没有匹配返回null;不是全局的情况下与match()方法返回的数值相同

var str="hello world";

console.log(/hello/.exec(str));//["hello", index: 0, input: "hello world"]

//第一为匹配的内容,第二为匹配的索引位置,第三为字符串本身

console.log(/hi/.exec(str));//null

console.log(/ello/.exec(str).index);//1 //index和input可以直接作为该方法返回的两个属性

 

三、JS中支持正则表达式的String对象方法

1、search()

  查找:stringObject.search(regexp/str);

  该方法用于检索字符串中指定的子字符串,或检索与正则表达式相匹配的字符串,它不执行全局匹配(将忽略标志g),也没有regexp对象的lastIndex属性,且总是从字符串开始位置进行查找,总是返回的是stringObject匹配的第一个位置

 

var str = "hello";

console.log(str.search(/l/)); //2 返回匹配到的第一个位置(使用的regexp对象检索)

console.log(str.search('l')); //2 使用字符串进行匹配

console.log(str.search(/l/g)); //2 没有全局的概念 总是返回匹配到的第一个位置

console.log(str.search(/L/i)); //2 可以忽略大小写来检索

console.log(str.search(/a/)); //-1,如果没有检索到的话,则返回-1

 

2、match()

  匹配:stringObject.match(regexp/str)

  该方法用于在字符串内检索指定的值,或找到一个或者多个正则表达式的匹配。该方法类似于indexOf()或者lastIndexOf(); 但是它返回的是指定的值,而不是字符串的位置;

var str = "hello";

console.log(str.match(/e/)); //["e", index: 1, input: "hello"] 返回一个数组,包括匹配到的字符,位置和自身

console.log(str.match('e'));//["e", index: 1, input: "hello"] 支持用字符串代替正则进行匹配

console.log(str.match(/l/g));//["l", "l"]支持全局匹配,返回匹配到的字符组成的数值

 

3、replace()

  替换:stringObject.replace(regexp/str,str/function);

  该方法用于在字符串中使用一些字符替换另一些字符,或者替换一个与正则表达式匹配的子字符串;默认只替换第一个匹配的字符,可以加修饰符g进行全局替换。整体返回替换后的新字符串。

var str = "hello world";

console.log(str.replace("hello","a"));// a world,使用字符串匹配并替换

console.log(str.replace(/hello/,"b"));// b world,使用正则匹配并替换

console.log(str.replace(/l/g,""));// heo word,使用正则加装饰符g进行全局替换

console.log(str.replace(/(hello)[ ](\w+)/g,"$2 $1"));// world hello,$1,$2 代表正则中第一个和第二个分组所匹配的文本

//第二个参数是function函数

console.log(str.replace(/o/g,function(v){

  console.log(v);//打印2次o

  return '-';

}));//hell- w-rld,匹配替换为函数的返回值

console.log(str.replace(/(e).*(o)\b/g,function(){

  console.log(arguments);//["ello", "e", "o", 1, "hello world"]

  //第一个匹配的字符,第二和第三是分组匹配的内容,第四是匹配索引位置,第5个是原字符串

  return 'i';

}));// hi world

 

4、split()

  分割:stringObject.split(regexp/substr,[length]);

  该方法把一个字符串分割成字符串数组,length设定返回数组的长度即超出部分将被忽略(可选参数)

 

var str = "hello world";

console.log(str.split(" ");//["hello", "world"],使用字符分割为数组

console.log(str.split(/o/g));//["hell", " w", "rld"],使用正则匹配的内容来分割

console.log(str.split(/o/g,2));//["hell", " w"],指定返回数组的length

 

posted on 2018-03-02 10:35  ranyonsue  阅读(652)  评论(0编辑  收藏  举报

导航