自学通用方法- 正则表达式
正则表达式是使用单个字符串来描述、匹配一系列或某个句法规则的字符串。它包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为"元字符")。正则表达式是一种文字模式,可以作为一个模板,与需要搜索的字符串进行匹配。
这里推荐一个很好的正则表达式的学习地方:
学习正则表达式不需要开发环境,可以在测试工具中学习。
字符的分类
正则表达式中,将各字符分为 元字符 和 普通字符 ,
普通字符
普通字符包括没有显式指定为元字符的所有可打印和不可打印字符。这包括所有大写和小写字母、所有数字、所有标点符号和一些其他符号。
元字符
元字符 是具有固定含义的特殊符号,包含了特殊字符、转义字符、限定符和定位符。每个元字符默认只匹配字符串中的一个字符。
- 特殊字符即单个字符有固定的含义。
- 转义字符是包含了反斜杠(\)的普通字符,组合起来有固定的含义。
- 限定符用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。有 * 或 + 或 ? 或 {n} 或 {n,} 或 {n,m} 共6种。
- 定位符使您能够将正则表达式固定到行首或行尾。它们还使您能够创建这样的正则表达式,这些正则表达式出现在一个单词内、在一个单词的开头或者一个单词的结尾。定位符用来描述字符串或单词的边界,^ 和 $ 分别指字符串的开始与结束,\b 描述单词的前或后边界,\B 表示非单词边界。
这里列举一些常用的元字符:
元字符 | 含义 |
---|---|
. | 匹配除换行符以外的任意字符 |
\w | 匹配字母或数字或下划线 |
\W | 匹配非字母或数字或下划线 |
\s | 匹配任意的空白符即[\t\n\r\f\v] |
\S | 匹配任意的非空白符 |
\d | 匹配任意数字 |
\D | 匹配非数字 |
\n | 匹配一个换行符 |
\t | 匹配一个制表符(tab) |
^ | 需要匹配的字符串的开始(定位符),在字符集的方括号内则表示不接受该字符集(反义) |
$ | 需要匹配的字符串的结尾(定位符) |
\b | 匹配一个单词边界,即字与空格间的位置(定位符) |
\B | 非单词边界匹配(定位符) |
() | 子表达式的开始和结尾,即分组 |
[] | 字符集的开始和结尾 |
* | 匹配此元字符前的子表达式零次或多次(限定符),等价于{0,} |
+ | 匹配此元字符前的子表达式一次或多次(限定符),等价于 {1,} |
? | 匹配此元字符前的子表达式零次或一次(限定符),等价于 {0,1} |
{} | 标记限定符表达式的开始和结束 |
{n} | n 是一个非负整数。匹配确定的 n 次(限定符) |
{n,} | n 是一个非负整数。至少匹配 n 次(限定符) |
{n,m} | m 和 n 均为非负整数,其中 n <= m。最少匹配 n 次且最多匹配 m 次(限定符) |
\ | 将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符 |
| | 指明两项之间的一个选择,如 a|b 则表示匹配 a 或 b |
\1 | 引用分组() 包含的部分,1表示引用第一个分组,2表示引用第二个 |
字符集
字符集可以当成一类特殊的元字符,若干普通字符或元字符使用方括号 [] 包裹起来就成为了字符集,表示匹配符合字符集中规则的字符。例如:
字符集 | 含义 |
---|---|
[a-zA-Z0-9_] | 匹配小写字母、大写字母、数字、下划线 |
[a-gA-G] | 匹配小写字母abcdefg和大写字母ABCDEFG |
[^ABC] | 不匹配大写字母ABC |
语法规则
正则表达式使用 元字符 、普通字符 通过一定的规则进行排列组合用来匹配字符串。
匹配字符串
类似于各个工具中的查找功能,当正则表达式完全由普通字符组成,则是进行字符串的完全匹配。例如:
待匹配字符串 | 正则表达式 | 匹配次数 | 匹配结果 |
---|---|---|---|
我的电话号码10086 | 100 | 1 | 100 |
带元字符的匹配
类似于查找功能中使用通配符,可以将元字符代替需查找的字符。需注意的是,元字符默认默认只匹配字符串中的一个字符。
待匹配字符串 | 正则表达式 | 匹配次数 | 匹配结果 |
---|---|---|---|
我的电话号码10086 | \d\d\d | 1 | 100 |
我的电话号码10086 | \d\d | 2 | 10,08 |
由例子可见,正则表达式的匹配是将每次匹配结果剔除后的字符串再进行匹配的。
贪婪匹配
正则表达式中可以使用 .* 进行贪婪匹配。贪婪匹配就是尽可能多的进行匹配。因为正则表达式的匹配是将每次匹配结果剔除后的字符串再进行匹配,所以贪婪匹配造成的结果就是匹配结果尽可能少,每项结果尽可能长。例如:
待匹配字符串 | 正则表达式 | 匹配次数 | 匹配结果 |
---|---|---|---|
今天去哪里玩?玩什么?玩完还有什么项目?好玩不? | 玩.* | 1 | 玩?玩什么?玩完还有什么项目?好玩不? |
干嘛呢?玩游戏。玩什么游戏?玩吃鸡游戏。晚上一起上游戏。 | 玩.*游戏 | 1 | 玩游戏。玩什么游戏?玩吃鸡游戏。晚上一起上游戏。 |
惰性匹配
惰性匹配和贪婪匹配正好相反,惰性匹配会尽可能少的匹配每一项,使得匹配结果尽可能多。惰性匹配使用 .*? 。例如:
待匹配字符串 | 正则表达式 | 匹配次数 | 匹配结果 |
---|---|---|---|
今天去哪里玩?玩什么?玩完还有什么项目?好玩不? | 玩.*? | 4 | 玩,玩,玩,玩 |
干嘛呢?玩游戏。玩什么游戏?玩吃鸡游戏。晚上一起上游戏。 | 玩.*?游戏 | 3 | 玩游戏,玩什么游戏,玩吃鸡游戏 |
选择
可以使用 | 来进行选择,例如:
说明 | 规则 | 正则表达式 | 备注 |
---|---|---|---|
匹配 1~99 的正整数 | 第1位为非0数,第2位为任意(或没有) | [1-9][0-9]? | |
匹配2位数 | 2位的数字 | [0-9]{2} | 第一位是0时也进行匹配 |
匹配0~99的正整数 | 第1位为0则没有第2位 | 0|[1-9][0-9]? | 0或者[1-9][0-9]?匹配哪个都可以 |
国内区号 | 3位数字或4位数字 | \d{3}|\d{4} |
子表达式(分组)
可以使用小括号 () 将一系列正则表达式括起来,表示一个组,进行组合使用。例如:
- 在选择中使用:
(exp1) | (exp2)
表示 表达式1或表达式2 选择一个。 - 配合限定符:
(exp){n}
将表达式重复 n 次
默认情况下,每个分组会有一个组号,规则是从左向右,第一个出现的分组的组号为1,第二个为2,以此类推。不过分组并不是这么简单的,还遵循以下原则:
- 分组0对应整个正则表达式
- 实际上组号分配过程是要从左向右扫描两遍的:第一遍只给未命名组分配,第二遍只给命名组分配。因此所有命名组的组号都大于未命名的组号
- 可以使用(?:exp)这样的语法来剥夺一个分组对组号分配的参与权.
后向引用
后向引用用于重复搜索前面某个分组匹配的文本。例如,\1代表分组1匹配的文本。具体示例:\b(\w+)\b\s+\1\b : \b 匹配单词边界, \w+ 匹配单词或数字或下划线, \s+ 匹配空白符,然后 \1 匹配分组1即(\w+)的内容,所以正则表达式表示匹配一个以空格分隔的重叠的普通字符串。
也可以自己指定子表达式的组名。要指定一个子表达式的组名,请使用这样的语法:(?<Word>\w+)(或者把尖括号换成’也行:(?‘Word’\w+)),这样就把\w+的组名指定为Word了。要反向引用这个分组捕获的内容,可以使用\k<Word>,所以上一个例子也可以写成这样:\b(?<Word>\w+)\b\s+\k<Word>\b。
零宽断言
零宽断言用于查抄在某些内容(不包括这些内容)之前或之后的东西,类似于定位符用于指定一个位置,这个位置应该满足一定的条件(即断言)。例如:
(?=exp)
也叫零宽度正预测先行断言,它断言自身出现的位置的后面能匹配表达式exp。比如\b\w+(?=ing\b),匹配以ing结尾的单词的前面部分(除了ing以外的部分),如查找I’m singing while you’re dancing.时,它会匹配sing和danc。(?<=exp)
也叫零宽度正回顾后发断言,它断言自身出现的位置的前面能匹配表达式exp。比如(?<=\bre)\w+\b会匹配以re开头的单词的后半部分(除了re以外的部分),例如在查找reading a book时,它匹配ading。
负向零宽断言
查找不是某个字符或不再某个字符集里的字符可以使用反义,如果只是想要确保某个字符没有出现,但并不想去匹配它时怎么办?例如,想查找这样的单词:它里面出现了字母q,但是q后面跟的不是字母u,我们可以尝试这样:\b\w*q[^u]\w*\b 。但是如果 q 处于单词结尾则会出错,因为 [^u] 总要匹配一个字符。所以当 q 处于单词结尾,则 [^u] 将会匹配单词分隔符(空格或句号或其他),后面的 \w*\b 将会匹配下一个单词,例如 Iraq fighting 将会被匹配。负向零宽断言可以解决这个问题,因为它只匹配一个位置,并不消费任何字符,所以应该使用 \b\w*q(?!u)\w*\b。
- 零宽度负预测先行断言
(?!exp)
,断言此位置的后面不能匹配表达式exp。例如:\d{3}(?!\d)匹配三位数字,而且这三位数字的后面不能是数字;\b((?!abc)\w)+\b匹配不包含连续字符串abc的单词。 (?<!exp)
零宽度负回顾后发断言可以断言此位置的前面不能匹配表达式exp:(?<![a-z])\d{7}匹配前面不是小写字母的七位数字。
一个更复杂的例子:(?<=<(\w+)>).*(?=<\/\1>)匹配不包含属性的简单HTML标签内里的内容。(?<=<(\w+)>)指定了这样的前缀:被尖括号括起来的单词(比如可能是<b>),然后是.*(任意的字符串),最后是一个后缀(?=<\/\1>)。注意后缀里的\/,它用到了前面提过的字符转义;\1则是一个反向引用,引用的正是捕获的第一组,前面的(\w+)匹配的内容,这样如果前缀实际上是<b>的话,后缀就是</b>了。整个表达式匹配的是<b>和</b>之间的内容(再次提醒,不包括前缀和后缀本身)。
注释
小括号的另一种用途是通过语法(?#comment)来包含注释。例如:2[0-4]\d(?#200-249)|25[0-5](?#250-255)|[01]?\d\d?(?#0-199)。
分组常用方法总结
分类 | 代码/语法 | 说明 |
---|---|---|
捕获 | (exp) | 匹配exp,并捕获文本到自动命名的组里 |
捕获 | (?<name>exp) | 匹配exp,并捕获文本到名称为name的组里,也可以写成(?'name’exp) |
捕获 | (?:exp) | 匹配exp,不捕获匹配的文本,也不给此分组分配组号 |
零宽断言 | (?=exp) | 匹配exp前面的位置 |
零宽断言 | (?<=exp) | 匹配exp后面的位置 |
零宽断言 | (?!exp) | 匹配后面跟的不是exp的位置 |
零宽断言 | (?<!exp) | 匹配前面不是exp的位置 |
注释 | (?#comment) | 这种类型的分组不对正则表达式的处理产生任何影响,用于提供注释让人阅读 |
非捕获元
一些使用示例
字符集的使用
说明 | 规则 | 正则表达式 | 备注 |
---|---|---|---|
匹配手机号 | 手机号为11位,第一位为1,第2位为3、5、8,第3位至11位都有 | 1[358]\d{9} | |
匹配 1~99 的正整数 | 第1位为非0数,第2位为任意(或没有) | [1-9][0-9]? |
定位符的使用
定位符能够帮正则表达式进行定位。例如:
待匹配字符串 | 正则表达式 | 匹配次数 | 匹配结果 |
---|---|---|---|
这个示例是一个正则表达式示例,这个示例里的文字只在这里作为例子。这个示例中的文字没有任何意义。 | ^.*?示例… | 1 | 这个示例是一个 |
这个示例是一个正则表达式示例,这个示例里的文字只在这里作为例子。这个示例中的文字没有任何意义。 | .*?示例… | 4 | 这个示例是一个/正则表达式示例,这个/示例里的文/字只在这里作为例子。这个示例中的文 |
hi high him history | hi | 4 | hi/hi/hi/hi |
hi high him history | \bhi\b | 1 | hi |
^ 表示字符串开始,$ 表示字符串结尾,\b 表示单词边界,即字与空格间的位置,\B 表示非单词边界
注:定位符不能和限定符一起使用,因为起始或结尾不能有多个。