正则表达式是处理字符串最有效的方法之一。最简单的例子:字符串“abc”,正则:b,匹配结果:“b”,在该正则中,会去先匹配字符串的第一个字符a,匹配失败,然后去匹配第二个字符b,匹配成功,输出结果(如果没有全局查找的声明,只会查找第一个符合条件的字串)。
限定符
日常使用时往往需要各种限制条件来完成更加复杂的匹配。使用限定符,用来表示匹配的次数,除三种常用的限定符外,用{}表示:
限定符 | 含义 | 举例 |
---|---|---|
* | 匹配前面的字符或表达式0次或无数次,等价于{0,},可以理解为无限制 | 字符:haaaa,正则:ha*,匹配结果:haaaa。如果使用正则ho*,则可以匹配到h。 |
+ | 匹配前面的字符或表达式至少1次,等价于{1,} | 字符:haaaa,正则:ha+,匹配结果:haaaa。如果使用正则ho+,则匹配失败,因为o一次也没有匹配到。 |
? | 匹配前面的字符或表达式最多1次,等价于{0,1} | 字符:haaaa,正则:ha?,匹配结果:ha。 |
{n} | 匹配前面的字符或表达式n次 | 如ha{3}可以匹配到haaa,匹配不到haa |
{n,m} | 匹配前面的字符或表达式最少n次,最多m次 | 如ha{1,3}可匹配到ha,haa,haaa。 |
{n,} | 匹配前面的字符或表达式最少n次 | 如ha{1,}等同于ha+。 |
贪婪和懒惰
*和+都是“贪婪的”,他们会尽可能多的匹配符合条件的字符,比如说字符串“<title>百度一下,你就知道</title>”,用正则<.*>可以匹配到整个字符串“<title>百度一下,你就知道</title>”,即从第一个“<”开始,到最后一个“>”之间的所有内容,如果只需要匹配第一个标签中的内容,即“<title>”,在正则中加入“?”即可实现最小匹配:<.*?>。
同理,+和?以及{n,m}都可以与?结合实现最小匹配。如字符串aaaabc,正则a??(等同于a{0,1}?),结果是空,因为正则会尽量少匹配,而去取0次匹配这个值。
定位符
定位符用来描述字符串或者单词的边界
定位符 | 含义 | 举例 |
---|---|---|
^ | 匹配字符串开头(在集合中出现表示否定) | 正则^a可以匹配abc,aab等以a开头的字符 |
$ | 匹配字符串的结尾 | 正则a$可以匹配bca等以a结尾的字符串 |
\b | 匹配单词的边界,包括单词的开头和结尾 | \bapp可以匹配apple等以app开头的单词,le/b则可以匹配le结尾的单词 |
^定位字符串的开头,生效的对象是该符号之后的字符,$定位字符串的结尾,生效的对象是该符号之前的字符,两者可以同时使用精确匹配一个字符串。
\b用法与字符串首尾定位符类似,需注意该定位符出现的位置,会影响匹配的结果。与之相反,\B可以匹配非单词边界,如\Bapt可以匹配chapter中的apt,但不能匹配aptitude中的apt。
集合
集合用“[]”定义,表示可以被匹配的范围,如[123],表示可以匹配的字符只有1,2,3。[a-z]表示可以匹配所有的小写字母,在集合中使用^表示否定,如[^0-9] 表示除了0-9以外的字符。如果要匹配多个字符的话还是要一一列举,所以也可以使用一些特定的字符来表示:
字符 | 含义 |
---|---|
. | 除了换行符以外的任何字符 |
\d | 匹配一个数字,等价于[0-9] |
\D | 匹配除了0-9以外的字符,等价于[^0-9] |
\w | 匹配包括下划线在内的单个字符,等价于[A-Za-z0-9] |
\s | 匹配包括空格,换行等空白字符 |
如匹配任何一个正整数:^[1-9][\d]*$
匹配方式
匹配方式通常用于写在正则表达式的结尾,如:“/a+/g”。部分语言会作为参数进行传递,常用的有i:忽略大小写;g:全局模式;m:多行模式。
正如开头所提,不声明全局模式时,正则只会匹配到第一个符合条件的字符串。
分组
分组使用“()”定义,在“()”中的所有字符会被视为一个整体。比如正则(ab)+会将ab做为一个整体来使用,正则ab+只有b会受到限定符“+”的影响。在括号中可以使用“|”表示多个可选的分组,如(ab|cd)表示分组可以是ab或者cd。
负向引用
使用分组后,会将分组进行缓存,同时生成索引可供调用,索引从1开始,用\1表示,代表了第一个分组,最简单的应用就是查找相邻的重复字符,比如字符串:“It is is a book”,使用正则\b(\w+)\s\1\b,输出结果:is。(\w+)\s捕获一个或多个字母以及之后的空格,\1捕获之前已经缓存的分组,\b则确保捕获到的是一个完整的单词。
如果不需要缓存分组,使用(?:abc)即可。
注意:在实际应用中,如果存在“()”,匹配成功的话只会返回括号中的内容,多个括号会返回多个括号的内容,在无负向引用的情况下,使用(?:abc)可返回完整结果
消耗字符
一般情况下,正则表达式在匹配的过程中会消耗字符,即已经匹配过的字符不会再次匹配,比如,在全局模式下用正则a*去匹配字符串“haaa”,会得到三个结果:[’’, ‘aaa’, ‘’],而第三个空结果正是因为前两次匹配已经消耗了字符串,所以会出现空的情况。
如果使用零宽断言,则可以只匹配位置,不消耗字符:
(?=exp):匹配exp前面的位置。
(?<=exp):匹配exp后面的位置。
(?!exp):匹配后面不是exp的位置。
(?!<exp):匹配前面不是exp的位置。
注意:零宽断言匹配的只是字符的位置,而不是字符,所以单纯使用零宽断言输出为空。
举例:字符串"this is this a book is",找出重复的单词,全局模式下使用正则\b(\w+).*\1\b,输出结果This is this,并没有找到重复的is,因为在匹配this的时候已经将”this is this“消耗了,之后的匹配是从”this is this“之后的“a”开始。利用(?=exp)匹配位置:\b(\w+)(?=.*\1\b),可得到争取的结果:”this,is“。
转义
当遇到关键字时,需要使用”\“进行转义,比如”.“,”*“,”+“等本身带有意义的符号,如果需要表示这些符号,需进行转义,写作”\.“,”\*“,”\+“。