正则表达式

正则表达式就是匹配模式,要么匹配字符,要么匹配位置,接下来从这两个方面来说明基本的正则表达式。

正则匹配基础

创建正则的两种方式:构造函数和字面量

var rgx1 = new RegExp("hello");
var rgx2 = /hello/;

使用修饰符

  • i,忽略大小写
  • g,全局匹配
  • m,多行匹配
var rgx1 = new RegExp("hello", "gi");
var rgx2 = /hello/gi;

使用RegExp#test方法

判断正则是否匹配一个字符串:

var rgx = /hello/;
console.log( rgx.test("hello") );
console.log( rgx.test("world") );
console.log( rgx.test("hello world") );

\\ result
console.log: true
console.log: false
console.log: true

使用RegExp#exec方法

获取匹配的相关信息:

var rgx = /world/;
console.log( rgx.exec("world !!") );
console.log( rgx.exec("hello world") );
console.log( rgx.exec("hello") );

\\ result
console.log: ["world", index: 0, input: "world !!"]
console.log: ["world", index: 6, input: "hello world"]
console.log: null

使用String#replace方法

字符串替换方法。
比如替换第一个foo为qux:

var str = "foo foo";
console.log( str.replace("foo", "qux") );

\\ result
console.log: "qux foo"

使用String#search方法

获取第一个匹配的下标:

var str = "hello world";
console.log( str.search(/world/) );

\\ result
console.log: 6

使用String#match方法

获取匹配的相关信息(注意有没有g修饰符,返回的格式是不一样的):

var str = "abcabc";
console.log( str.match(/b/) );
console.log( str.match(/b/g) );

\\ result 
console.log: ["b", index: 1, input: "abcabc"]
console.log: ["b", "b"]

字符匹配

通常在正则中,精确匹配是非常少的情形。
比如/hello/,用来匹配字串中的hello子串。
通常的是模糊匹配。虽是“模糊”,但通常都有约束的存在。
所谓“约束”,也就是规则,在有规则下的模糊,才有意义。
本章将讨论两种常见的模糊,足以覆盖正则中80%的情形。
1.在纵向尺度上的模糊,一个字符可以是数字、字母等,而不是某一个具体的字符。
2.在横向尺度上的模糊,一个字符出现的次数可以定制,不是只是一次。
实现前者需要使用字符组,而要实现后者需要使用量词。

通配符匹配

比如要匹配这样一个序列,先是1,然后某个字符,再是3。也就是像123,1b3,1 3,133等等这样的字符。针对这种情形我们可以使用.来匹配。

var str = "123 1b3 1 3 133 321";
console.log( str.match(/1.3/g) );

匹配数字

通配符不是唯一的模糊匹配。例如前面的例子我们可以要求1和3之间的字符是数字。我们可以使用\d匹配符。

var str = "123 1b3 1 3 133 321";
console.log( str.match(/1\d3/g) );

\\ result
console.log: ["123", "133"]

匹配字母数字

此时我们要求1和3直接的字符是字母或数字。此时可以用单词匹配符\w。“单词”具体的含义是数字0-9、小写字母a-z、大写字母A-Z以及下划线_。

var str = "123 1b3 1 3 133 321";
console.log( str.match(/1\w3/g) );

\\ result
console.log: ["123", "1b3", "133"]

反义

\D是\d的反义版本,\D表示此字符是任何字符,但不是数字。即0-9之外的任意一个字符。\W与\w也是类似的关系。

范围表示法

有时,要匹配的东西,不像\w那么多种可能。比如要找bicycle中所有的a、b、c字母,正则可以用/[abc]/g,其中[abc]匹配一个字符,此字符可以是abc中的任何一个。

var str = "bicycle";
console.log( str.match(/[abc]/g) );

\\ result
console.log: ["b", "c", "c"]

注意:[abc]只是三个字母倒好说,但是26个字母呢?不能一一写下吧。还好我们有范围表示法,例如[a-z]表示所有小写字母。可以举个小场景。一堆长度为3个字母的英文名字,要判断格式是否正确。即首字母大写,其余俩字母小写,可以使用正则/[A-Z][a-z][a-z]/g

var str = "Tim sam Bob maC Guy";
console.log( str.match(/[A-Z][a-z][a-z]/g) );

\\ result
console.log: ["Tim", "Bob", "Guy"]

匹配出现至少一次

+表示出现的次数至少一次。比如匹配英文名字,首字母大写,至少两个字母,可以使用/[A-Z][a-z]+/g:

var str = "Timothy Dave max Bob Greg T";
console.log( str.match(/[A-Z][a-z]+/g) );

\\ result
console.log: ["Timothy", "Dave", "Bob", "Greg"]

匹配出现0次或者1次

?表示出现的次数0次或者一次。比如匹配苹果的单复数,可以使用/apples?/gi

var str = "Apple Apples Applse";
console.log( str.match(/apples?/gi) );

\\ result
console.log: ["Apple", "Apples"]

匹配出现0次或者任意次

*表示出现的次数0次或者任意次。比如查找日志中的update单词,其后可能有感叹号,也可能没有感叹号,甚至可能有多个,如updata!!!!!!!!,可以使用正则/update!*/g:

var str = "update update!!! update!!!!!!!!";
console.log( str.match(/update!*/g) );

\\ result
console.log: ["update", "update!!!", "update!!!!!!!!"]

匹配出现n次

有时,你需要自定定义次数。比如匹配电话号码123-4567,你可能会把正则写成/\d\d\d-\d\d\d\d/,其实改写成/\d{3}-\d{4}/,这样可读性比较高。{n}表示出现n次。

匹配至少出现n次

比如,``{n,}\``表示至少出现n次。例如你希望密码的长度最小为6,可以用```/\w{6,}/g```:

var str = "pass password long\_password";  
console.log\( str.match\(/\w{6,}/g\) \);

console.log: \["password", "long\_password"\]

匹配出现n到m次

比如:{n,m}表示出现n到m次。比如一般评论都是15到140字。可以用/.{15,140}/

匹配分支

有时匹配规则并不是一个字符多选一,而是另外一种多选一情形。比如匹配yes和no。可以按照之前的思路,可能会把正则写成/[yn][eo]s?/g。虽然匹配了yes和no,但也匹配了其他情形,比如nos。

显然上面的结果不是我们想要的。此时需要使用分支结构,使用管道符“|”,如/yes|no/g,下面我们会看到也会匹配nos中的no,下一章会解决该问题。

var str = "yes no nos ne nes yos ye yo";  
console.log\( str.match\(/yes\|no/g\) \);

\ result  
console.log: \["yes", "no", "no"\]

匹配位置

什么是位置

位置是相邻字符之间的位置。比如,下图中箭头所指的地方:

如何匹配位置

在ES5中,共有6个锚字符:

^ $ \b \B (?=p) (?!p)

^和$

^(脱字符)匹配开头,在多行匹配中匹配行开头。
$(美元符号)匹配结尾,在多行匹配中匹配行结尾。
比如我们把字符串的开头和结尾用"#"替换(位置可以替换成字符的!):

var result = "hello".replace(/^|$/g, '#');
console.log(result); // "#hello#"

\\ result
console.log: "#hello#"

\b和\B

\b是单词边界,具体就是\w和\W之间的位置,也包括\w和^之间的位置,也包括\w和$之间的位置。
比如一个文件名是"[JS] Lesson_01.mp4"中的\b,如下:

var result = "[JS] Lesson_01.mp4".replace(/\b/g, '#');
console.log(result); // "[#JS#] #Lesson_01#.#mp4#"

\\ result
console.log: "[#JS#] #Lesson_01#.#mp4#"

为什么是这样呢?这需要仔细看看。
首先,我们知道,\w是字符组[0-9a-zA-Z]的简写形式,即\w是字母数字或者下划线的中任何一个字符。而\W是排除字符组[^0-9a-zA-Z]的简写形式,即\W是\w以外的任何一个字符。
此时我们可以看看"[#JS#] #Lesson_01#.#mp4#"中的每一个"#",是怎么来的。
第一个"#",两边是"["与"J",是\W和\w之间的位置。
第二个"#",两边是"S"与"]",也就是\w和\W之间的位置。
第三个"#",两边是空格与"L",也就是\W和\w之间的位置。
第四个"#",两边是"1"与".",也就是\w和\W之间的位置。
第五个"#",两边是"."与"m",也就是\W和\w之间的位置。
第六个"#",其对应的位置是结尾,但其前面的字符"4"是\w,即\w和$之间的位置。

\B就是\b的反面的意思,非单词边界。例如在字符串中所有位置中,扣掉\b,剩下的都是\B的。
具体说来就是\w与\w、\W与\W、^与\W,\W与$之间的位置。
比如上面的例子,把所有\B替换成"#":

var result = "[JS] Lesson_01.mp4".replace(/\B/g, '#');
console.log(result); // "#[J#S]# L#e#s#s#o#n#_#0#1.m#p#4"

\\ result
console.log: "#[J#S]# L#e#s#s#o#n#_#0#1.m#p#4"

(?=p)和(?!p)

(?=p),其中p是一个子模式,即p前面的位置。
比如(?=l),表示'l'字符前面的位置,例如:

var result = "hello".replace(/(?=l)/g, '#');
console.log(result); // "he#l#lo"

位置的特性

对于位置的理解,我们可以理解成空字符""。
比如"hello"字符串等价于如下的形式:

"hello" == "" + "h" + "" + "e" + "" + "l" + "" + "l" + "o" + "";

也等价于:

"hello" == "" + "" + "hello"

正则速查表

表达式 含义
\ 将下一个字符标记为一个特殊字符、或一个原义字符、或一个向后引用、或一个八进制转义符。例如,“n"匹配字符"n"。"\n"匹配一个换行符。串行"\"匹配"\"而"("则匹配"("。
^ 匹配输入字符串的开始位置。如果设置了RegExp对象的Multiline属性,^也匹配“\n"或"\r"之后的位置。
$ 匹配输入字符串的结束位置。如果设置了RegExp对象的Multiline属性,$也匹配“\n"或"\r"之前的位置。
\* 匹配前面的子表达式零次或多次。例如,zo*能匹配“z"以及"zoo"。*等价于{0,}。
+ 匹配前面的子表达式一次或多次。例如,"zo+"能匹配"zo"以及"zoo",但不能匹配"z"。+等价于{1,}。
? 匹配前面的子表达式零次或一次。例如,"do(es)?"可以匹配"does"或"does"中的"do"。?等价于{0,1}。
{n} n是一个非负整数。匹配确定的n次。例如,"o{2}"不能匹配"Bob"中的"o",但是能匹配"food"中的两个o。
{n,} n是一个非负整数。至少匹配n次。例如,"o{2,}"不能匹配"Bob"中的"o",但能匹配"foooood"中的所有o。"o{1,}"等价于"o+"。"o{0,}"则等价于"o*"。
{n,m} m和n均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。例如,"o{1,3}"将匹配"fooooood"中的前三个o。"o{0,1}"等价于"o?"。请注意在逗号和两个数之间不能有空格。
? 当该字符紧跟在任何一个其他限制符(*,+,?,{n},{n,},{n,m})后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串"oooo","o+?"将匹配单个"o",而"o+"将匹配所有"o"。
. 匹配除"\n"之外的任何单个字符。要匹配包括"\n"在内的任何字符,请使用像"(.\ \n)"的模式。
(pattern) 匹配pattern并获取这一匹配。所获取的匹配可以从产生的Matches集合得到,在VBScript中使用SubMatches集合,在JScript中则使用$0…$9属性。要匹配圆括号字符,请使用"("或")"。
(?:pattern) 匹配pattern但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用或字符“(\ )"来组合一个模式的各个部分是很有用。例如"industr(?:y ies)"就是一个比"industry industries"更简略的表达式。
(?=pattern) 正向肯定预查,在任何匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如,```"Windows(?=95 98 NT 2000)"```能匹配"Windows2000"中的"Windows",但不能匹配"Windows3.1"中的"Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。
(?!pattern) 正向否定预查,在任何不匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如```"Windows(?!95 98 NT 2000)"```能匹配"Windows3.1"中的"Windows",但不能匹配"Windows2000"中的"Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始
(?<=pattern) 反向肯定预查,与正向肯定预查类拟,只是方向相反。例如,`"(?<=95 98 NT 2000)Windows"` 能匹配"2000Windows"中的"Windows",但不能匹配"3.1Windows"中的"Windows"。
(?<!pattern) 反向否定预查,与正向否定预查类拟,只是方向相反。例如`"(?<!95 98 NT 2000)Windows"`能匹配"3.1Windows"中的"Windows",但不能匹配"2000Windows"中的"Windows"。
`x\ y` 匹配x或y。例如,“z\ food"能匹配"z"或"food"。"(z\ f)ood"则匹配"zood"或"food"。
[xyz] 字符集合。匹配所包含的任意一个字符。例如,“[abc]"可以匹配"plain"中的"a"。
[^xyz] 负值字符集合。匹配未包含的任意字符。例如,"[^abc]"可以匹配"plain"中的"p"。
[a-z] 字符范围。匹配指定范围内的任意字符。例如,“[a-z]"可以匹配"a"到"z"范围内的任意小写字母字符。
[^a-z] 负值字符范围。匹配任何不在指定范围内的任意字符。例如,"[^a-z]"可以匹配任何不在"a"到"z"范围内的任意字符。
\b 匹配一个单词边界,也就是指单词和空格间的位置。例如,“er\b"可以匹配"never"中的"er",但不能匹配"verb"中的"er"。
\B 匹配非单词边界。“er\B"能匹配"verb"中的"er",但不能匹配"never"中的"er"。
\cx 匹配由x指明的控制字符。例如,\cM匹配一个Control-M或回车符。x的值必须为A-Z或a-z之一。否则,将c视为一个原义的“c"字符。
\d 匹配一个数字字符。等价于[0-9]
\D 匹配一个非数字字符。等价于[^0-9]
\f 匹配一个换页符。等价于\x0c和\cL。
\n 匹配一个换行符。等价于\x0a和\cJ。
\r 匹配一个回车符。等价于\x0d和\cM。
\s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于[ \f\n\r\t\v]
\S 匹配任何非空白字符。等价于[^ \f\n\r\t\v]
\t 匹配一个制表符。等价于\x09和\cI。
\v 匹配一个垂直制表符。等价于\x0b和\cK。
\w 匹配包括下划线的任何单词字符。等价于"[A-Za-z0-9_]"
\W 匹配任何非单词字符。等价于"[^A-Za-z0-9_]"
\xn 匹配n,其中n为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如,“\x41"匹配"A"。"\x041"则等价于"\x04&1"。正则表达式中可以使用ASCII编码。
\num 匹配num,其中num是一个正整数。对所获取的匹配的引用。例如,“(.)\1"匹配两个连续的相同字符。
\n 标识一个八进制转义值或一个向后引用。如果\n之前至少n个获取的子表达式,则n为向后引用。否则,如果n为八进制数字(0-7),则n为一个八进制转义值。
\nm 标识一个八进制转义值或一个向后引用。如果\nm之前至少有nm个获得子表达式,则nm为向后引用。如果\nm之前至少有n个获取,则n为一个后跟文字m的向后引用。如果前面的条件都不满足,若n和m均为八进制数字(0-7),则\nm将匹配八进制转义值nm。
\nml 如果n为八进制数字(0-3),且m和l均为八进制数字(0-7),则匹配八进制转义值nml。
\un 匹配n,其中n是一个用四个十六进制数字表示的Unicode字符。例如,\u00A9匹配版权符号(©)。

results matching ""

    No results matching ""