正则表达式 - 2018-08-07
元字符 metacharacter
- \b : 匹配单词的开始或结尾,只匹配一个位置
 - . (点) 匹配除换行符以外的任何字符
 - \d 匹配数字 0-9
 - \s 匹配任意的空白符,包括空格、制表符、换行符、中文全角空格等
 - \w 匹配字母、数字、下划线
 - ^ 匹配字符串的开始
 - $匹配字符串的结束,比如 一个网站要求你填写的QQ号必须为5-12位数字时,可以使用^\d{5,12}$,单独的{2}表示不多不少重复2次,{5,12}表示重复的次数不能少于5次,不能多余12次。
如果选中了处理多行的选项,^和$的意义为 匹配行的开始和结束处 - () 也是元字符,用来分组
 - 如果要查找元字符本身,比如 . 和 * ,就需要使用反斜杠 \ 来进行转义,查找反斜杠 \,也得用 \\
 
限定符 指定数量的代码
| 代码 | 说明 | 
|---|---|
| * | 重复 0 次或者重复多次 | 
| + | 重复 1 次或者重复多次 | 
| ? | 重复 0 次或者 1 次 | 
| 重复了 n 次 | |
| 重复了 n 次或者更多次 | |
| 重复 m 到 n次 | 
字符类
使用中括号 []
[aeiou]匹配任何一个元音字母[.?!]匹配标点符号 . ? ![0-9]匹配数字 0-9 与\d代表的意义相同[0-9a-zA-Z]与\w等价\(?0\d{2}[(-]?\d{8}首先是一个转义字符\(它出现0次或1次(?), 然后是数字 0,后面跟着 2 个数字 (\d{2}), 然后是空格或者-中的一个,它出现1次或者不出现(?),最后是8个数字(\d{8}) 。上式可以匹配010)88886666,或022-22334455,或02912345678等常见电话号码格式,但是也能匹配 010)12345678或(022-87654321这样的“不正确”的格式,因此就要使用下面的 分支条件
分支条件
用|号把不同的规则分隔开
e.g. \(0\d{2}\)[- ]?\d{8}|0\d{2}[- ]?\d{8}这个表达式匹配三位区号的电话号码,其中区号可以用小括号括起来,也可以不用,区号与本地号支架你可以用连字号或空格间隔,也可以不用。
注意: 匹配分支条件时,将会从左到右地测试每个条件,如果满足了某个条件将不会再去管其他的条件
比如:\d{5}-\d{4}|\d{5} 用于匹配美国的邮政编码,美国邮编的规则是5位数字或者用连字号间隔的9位数字;如果写成:
\d{5}|\d{5}-\d{4}那么就只会匹配5位的邮编,以及9位邮编的前五位
分组
e.g. 描述一个IP地址
((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)
要点:2[0-4]\d|25[0-5]|[01]?\d\d? 分为三种情况:
- 一个2,然后0-4中选一个,最后第三位任选(200-249)
 - 一个25,然后0-5中选一个 (250-255)
 - 0和1中选一个,可以不选,再是一个数字,最后第三位可有可没有(0-199)
最后再是一个\.表示IP四个数字之间的分隔,由于最后面一个数字不带点,所以要分开,先写前面3个 - 整个最外层的括号就代表分组,
{3}代表前面的部分重复三次 
反义
采用大写的方式,与原来的小写方式表达相反的含义
| 代码 | 说明 | 
|---|---|
\W | 
与\w相对应,匹配不是字母、数字、下划线的字符 | 
\S | 
与\s相对应,匹配任何不是空白符的字符 | 
\D | 
与\d相对应,匹配任意非数字的字符 | 
\B | 
与\b相对应,匹配不是单词开头或结束的位置 | 
[^x] | 
匹配除x以外的字符 | 
[^aeiou] | 
匹配除aeiou以外的任意字符 | 
后向引用
使用小括号指定一个子表达式后,匹配这个子表达式的文本(也就是此分组捕获的内容)可以在表达式或其它程序中作进一步的处理。默认情况下,每个分组会自动拥有一个组号,规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。
后向引用用于重复搜索前面某个分组匹配的文本,\1代表分组1匹配的文本
e.g.
- 
\b(\w+)\b\s+\1\b可以用来匹配重复的单词,像go go, 或者kitty kitty。这个表达式首先是一个单词,也就是单词开始处和结束处之间的多于一个的字母或数字(
\b(\w+)\b),这个单词会被捕获到编号为1的分组中,然后是1个或几个空白符(\s+),最后是分组1中捕获的内容(也就是前面匹配的那个单词)(\1)。 - 
也可以自己指定表达式的组名,e.g.指定一个表达式的组名为
Word,则可以使用(?<Word>\w+),其中尖括号也可以用引号'来替代,即(?'Word'\w+),当反向需要引用这个分组捕获的内容时,可以用\k<Word> 
| 代码 | 说明 | 
|---|---|
(exp) | 
匹配 exp ,并捕获文本到自动命名的组里 | 
(?<name>exp) | 
匹配 exp ,并捕获文本到名称为 name 组里,也可以(?'name'exp) | 
?:exp | 
匹配 exp ,不捕获匹配的文本,也不给此分组分配组号 | 
零宽断言
(?=exp)它断言自身出现的位置后面可以匹配表达式 exp ,比如:\b\w+(?=ing\b)匹配以 ing 结尾的单词的前面部分(除了 ing 以外的部分),比如在查找 I am singing while you are dancing. 它会匹配 sing 和 danc,称之为:零宽度正预测先行断言(?<=exp)它断言自身出现的位置前面可以匹配表达式 exp ,比如(?<=\bre)\w+\b,会匹配以 re 开头的单词的后半部分(除 re 以外的部分),比如在查找 *reading a book *,会匹配 ading。称之为零宽度正回顾后发断言
e.g. (?<=\s)\d+(?=\s) 匹配以空白字符间隔的数字(不包括这些空白字符)
负向零宽断言
(?!exp)断言此位置的后面不能匹配表达式 exp
e.g.
\d{3}(?!\d)匹配三位数字,而且这三位数字的后面不能是数字
\b((?!abc)\w)+\b匹配不包含连续字符串 abc 的单词
一个更复杂的例子:(?<=<(\w+)>).*(?=<\/\1>)
匹配不包含属性的简单HTML标签内的内容;(?<=<(\w+)>)指定了这样的前缀:被尖括号括起来的单词;然后是.*,即任意字符串,最后是一个后缀?=<\/\1>,这里\/(斜杠和反斜杠)b用到了前面的字符转义;\1用到了反向引用,引用的正是捕获的第一组\w+匹配的内容,这样,如果前缀是, 那后缀就是 ;整个表达式匹配的是和之间的内容(不包括前缀和后缀)
注释
- 小括号的一种用途是通过
?#comment来包含注释,例如:
2[0-4]\d(?#200-249)|25[0-5](?# 250-255)|[01]?\d\d?(?#0-199) - 要包含注释的话,最好是启用“忽略模式里的空白符”选项,这样在编写表达式时能任意的添加空格,Tab,换行,而实际使用时这些都将被忽略。启用这个选项后,在#后面到这一行结束的所有文本都将被当成注释忽略掉。例如,我们可以前面的一个表达式写成这样:
 
(?<=     #断言要匹配的文本的前缀
<(\w+)>  #查找尖括号括起来的字母或数字(即HTML或XML标签)
)             #前缀结束
.*            #匹配任意文本
(?=          #断言要匹配的文本的后缀
<\/\1>       #查找尖括号括起来的内容
)               #后缀结束
贪婪与懒惰
- 表达式 
a.*b将匹配 最长的以 a 开始,以 b 结束的字符串,如果用其来搜索 aabab 它会匹配整个字符串;这被称之为 贪婪匹配,匹配 - 有时我们需要匹配尽可能少的字符,因此可在后面加一个限定符
?,即.*?表示匹配任意数量的重复,但是在能使整个匹配成功的前提下使用最少的重复;
比如:a.*?b匹配最短的,以 a 开始,以 b 结束的字符串;如果将其应用于 aabab,它会匹配到第一个aad(第1个字符到第3个字符)和 ab (第4-5个字符)- 为什么第一个匹配是aab(第一到第三个字符)而不是ab(第二到第三个字符)?简单地说,因为正则表达式有另一条规则,比懒惰/贪婪规则的优先级更高:最先开始的匹配拥有最高的优先权——The match that begins earliest wins。
 
 
| 代码 | 说明 | 
|---|---|
*? | 
重复任意次,但是尽可能少的重复 | 
+? | 
重复1次或更多次,但尽可能少的重复 | 
?? | 
重复0次或1次,但尽可能少重复 | 
{m,n}? | 
重复 m-n 次,但是尽可能少重复 | 
{n,}? | 
重复n次以上,但是尽可能少重复 | 
平衡组/递归匹配
有时我们需要匹配像( 100 * ( 50 + 15 ) )这样的可嵌套的层次性结构,这时简单地使用(.+)则只会匹配到最左边的左括号和最右边的右括号之间的内容(这里我们讨论的是贪婪模式,懒惰模式也有下面的问题)。假如原来的字符串里的左括号和右括号出现的次数不相等,比如( 5 / ( 3 + 2 ) ) ),那我们的匹配结果里两者的个数也不会相等。有没有办法在这样的字符串里匹配到最长的,配对的括号之间的内容呢?
为了避免(和(把你的大脑彻底搞糊涂,我们还是用尖括号代替圆括号吧。现在我们的问题变成了如何把xx <aa 
这里将用到一下语法:
(?'group')把捕获的内容命名为group,并压入堆栈(stack)(?'-group')从堆栈上弹出最后压入堆栈的名为 group 的捕获内容,如果堆栈为空,则本组的匹配失败(?(group)yes|no)如果堆栈上存在以名为group的不捕获内容的话,继续匹配 yes 部分的表达式,否则匹配 no 部分(?!)零宽负向先行断言,由于没有后缀表达式,试图匹配总是失败
思路:每碰到左括号,就压入一个 "Open" ,每碰到一个右括号,就弹出一个,到了最后看堆栈是否为空,如果不为空则证明左括号比右括号多,那匹配就应该失败。正则表达式会进行回溯(放弃最前面或最后面的一些字符),尽量使整个班表达式得到匹配
<				#最外层的左括号
	[^<>]*		#最外层的左括号后面的不是括号的内容
	(
	 	(
		 	(?'Open'<)	#匹配到了左括号,往堆栈压入一个'Open'
			[^<>]*		#匹配左括号后面不是括号的内容
		)+				#可以出现多次
		(
		 	(?'-Open'>)	#碰到了右括号,则从堆栈弹出一个'Open'
			(^<>)*		#匹配右括号后面不是括号的内容
		)+				#可以出现多次
	)*					#整个上面可以出现多次
	(?(Open)(?!))		#在遇到最外层的右括号前面,判断堆栈上是否还有"Open",
						#如果还有,则执行 (?!),由于后面没有后缀表达式,匹配总是失败
>						#最外层的括号

                
            
        
浙公网安备 33010602011771号