内容:如题
前置技能:None
参考教程:30分钟正则入门
前言
最近喜欢各种入门,这里入门一下正则表达式。字数很少比较水。
字符串可以说是编程是遇到的最多的一种数据结构,也是最简单的数据结构,对字符串操作的需求可谓无处不在。给人阅读的文本可以看做字符串,内存中存储的特定编码序列可以看做字符串,一个网页的html文本是字符串,浏览器对html文本解析的过程是字符串处理,无论是编译型语言还是解释型语言,编译和解释的过程都可以看做字符串处理。字符串操作重要性不言而喻。而在字符串中一种很常见的操作就是字符串的匹配。编写程序去处理特定的需要匹配的字符串可以做到,但耗时耗力,且代码不具有重用性。所以我们需要正则表达式。
正则表达式(Regular Expression)是一种匹配规则,用来匹配你想要的各种形式的字符串。规则有了还不行,还需要一个正则引擎来支持它,正则引擎可以看做一种编译器,实现需要用到编译原理的知识,支持语法越多越复杂,这里不讨论(有时间实现,挖坑,以后填)。
在Shell中我们常用*
来表示匹配所有项,比如ls *.v
表示列出该目录下所有.v
文件。*
是正则的语法,能这样用是因为Shell部分支持正则表达式。*
也称为通配符。
正则表达式是难于读写的,所以需要对其正确性进行测试,可以使用上面那篇教程的作者写的一个正则表达式测试器,也可以使用这个在线正则表达式测试器。
正则语法
元字符
首先如果想要匹配特定字符串,直接使用该字符串即可。
另外一些匹配字符或者位置的元字符:
\b
匹配一个位置,单词的开头或者结尾.
匹配除了换行符以外的任何字符\d
匹配一个数字,即0到9\s
匹配空白符,包括空格、制表符、换行等\w
匹配一个字母数字下划线或者汉字等^
匹配字符串开始的位置$
匹配字符串结尾的位置
其中\b
^
$
匹配位置而不是字符,就是说他们不匹配为一个字符,即不占一个字符的位置。
重复
代表重复的限定符不匹配字符,仅代表限定符前的字符可以重复的次数。
*
指定*
前面的内容可以使用任意次(0次或者多次)+
与*
类似,不过是1次或者多次?
重复0或者1次{n}
重复n次{n,}
重复n次或者更多次{n,m}
重复n到m次
eg: ^[1-9]\d{4,9}$
匹配一个5到10位的不以0开头的数字串,可以是QQ号
但是如果要匹配上面这些已经有特殊意义的字符呢?当然是转义了:\*
\.
\-
\\
字符类
使用[aeiou]
表示aeiou中的任何一个字符。
而[0-9]
就等同于\d
。这里可以使用-
表示范围。
分枝条件
使用|
表示或者。\d|[a-zA-Z]
匹配字母或者数字。
分组
使用()
进行分组,每个分组也称为子表达式。可以对每个子表达式使用上面的表示重复的语法。
如果要匹配(
或者)
,则需要转义\(
\)
。[]
{}
同理。
eg:匹配一个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?)
表示0到255。
反义
能匹配符合某种模式的,肯定也需要匹配不满足某种模式的。
\W
匹配任意不是字母数字下划线汉字的字符\S
匹配不是空白符的字符\D
匹配非数字字符\B
匹配非单词开始和结尾的任何位置[^X]
匹配除X以外的任何字符[^aeiou]
匹配除aeiou外的所以字符
后向引用
使用()
指定子表达式时,每个分组会自动拥有一个组号,从1开始依次增加。后向引用可以重复使用前面某个分组匹配的文本。使用\k
表示匹配编号为k的分组捕获的内容。
eg:匹配重复单词 \b(\w+)\b\s+\1\b
,注意是匹配重复单词,而不是两个单词,即是第一个分组捕获了一个单词并被\1
引用。
另外可以自己指定组名,使用语法 (?<Word>\w+)
或者 (?'Word'\w+)
则组名指定为 Word
,引用该分组捕获的内容则使用\k<Word>
。上面的例子:\b(?<Word>\w+)\b\s+\k<Word>\b
(?:exp)
匹配exp,但不捕获文本,也不分配组号。
零宽断言
(?=exp)
,零宽度正预测先行断言,断言自身出现的位置的后面能匹配表达式exp,即匹配表达式exp前面的位置。(?<=exp)
,零宽度正回顾后发断言,匹配表达式exp后面的位置(?!exp)
,零宽度负预测先行断言,匹配后面不是exp的位置(?<!exp)
,宽度正回顾后发断言,匹配前面不是exp的位置
忽略它们的名字,语法均是()
中以?
开头,然后有<
代表后面的位置,没有则代表前面,=
代表匹配,!
代表不匹配。注意它们都匹配位置而不是字符。
另外一个与()
相关的语法是注释:(?#comment)
贪婪与懒惰
上面的重复如*
{n,m}
等蕴含着一个问题。即是使用a.*b
来匹配字符串aabab
是得到aab
还是aabab
。答案是aabab
,因为默认情况下是贪婪匹配,即在匹配的情况下尽可能多重复。
在重复匹配的语法后面加上一个?
可以将其变为懒惰匹配,即尽可能少重复。语法:*?
+?
??
{n,}?
{n,m}?
处理选项
如果语法太死的话似乎会给人不够灵活的感觉。在.Net下,有一些处理选型可以改变处理正则的方式,比如在某些时候需要忽略大小写是很必要的。详见:正则表达式选项。
转义字符
匹配上面用到的特殊字符时都需要转义,另外ASCII编码本身定义的转义字符也是支持的。详见:正则表达式中的字符转义。