一、前沿
正则表达式的"祖先"可以一直上溯至对人类神经系统如何工作的早期研究。Warren McCulloch 和 Walter Pitts 这两位神经生理学家研究出一种数学方式来描述这些神经网络。
1956 年, 一位叫 Stephen Kleene 的数学家在 McCulloch 和 Pitts 早期工作的基础上,发表了一篇标题为"神经网事件的表示法"的论文,引入了正则表达式的概念。正则表达式就是用来描述他称为"正则集的代数"的表达式,因此采用"正则表达式"这个术语。
随后,发现可以将这一工作应用于使用 Ken Thompson 的计算搜索算法的一些早期研究,Ken Thompson 是 Unix 的主要发明人。正则表达式的第一个实用应用程序就是 Unix 中的 qed 编辑器。
而我们开发为什么要使用它,就是:方便!(比我们用一堆的循环加判断便捷好看的多)。
其实正则的涉及领域很广:像:C#,Java,JavaScript, Python, Ruby。其实内容的大同小异,当然不同的语言的正则看起来还是有些出入的。这里我就讲我们app这边遇到的能使用的一些正则语法使用。
对于一般开发来讲,使用的地方比较少,处于一种“可有可无”的状态,需要的时候直接网上搜来用,不太多的了解,从而正则显得有些神秘。
今天我们试着揭开这神秘的面纱。
二、术语
1.正则:正则表达式(regular expression)
2.匹配:一个正则表达式匹配一个字符串,指这个表达式能在这个字符串中找到匹配的文本。
3.元字符:一个字符是否是元字符取决于应用情况。正则的流派不一样他的定义也不一样。
4.流派(flavor):每样工具支持的元字符和其他的特性各有不同,表面上是看到的定义的元字符不一样,但是这里的流派远远不止这些。
5.子表达式:通常是括号内的表达式,或者是由|分割的多选分支。
6.字符:一个特殊定义的单词,这个值是由他解释所转化的编码来决定的。
三、正则里面的与或非
与:“与”是最简单的关系,它表示若干个元素必须同时相继出现,比如匹配单词cat,其实就是要求字符c、字符a和字符t必须同时连续出现。
或:“或”是正则表达式灵活性的重要体现,我们可以规定某个位置的文本的“多种可能”,比如要匹配cat或是cut,在正则表达式看来,就是“字符c,然后是a或u,然后是t”。正则表达式写做c[au]t,中间指定位置是单字符可以这样一一罗列,多字符可以使用(…|…)分割,例如:c(u|har|onduc|our)t
非:不容许出现某个或某几个字符,仍然用上面的例子,如果要匹配的单词是c开头、t结尾,中间有一个字符,但不能是u(也就是说,整个单词不能是cut),直接用c[^u]t就可以了,若中间的字符不能是a或u(也就是说,整个单词不能是cat或cut),则表达式改为c[^au]t。(c[^au]t:表示c和t之间不能(包含任何a和u的字母组合))
四、元字符
^:匹配输入字符串的开始位置(脱字符)
$:匹配输入字符串的结尾位置(美元符号)
():标记一个子表达式的范围
. :匹配除换行符 \n 之外的任何单字符,(通常被称为点号dot或者小点point), 要匹配包括 '\n' 在内的任何字符,请使用像”(.|\n)"的模式。
|:意思是“或”(一般用来将不同的子表达式组合成一个总的表达式)
?:代表可选项,把它加在一个字符后面,表示这个地方可以出现这个字符,也可不出现。
+:代表之前紧邻的元素出现一次或者多次
*:代表之前紧邻的元素出现任意多次或者不出现
{}:自定义前面内容重复出现的次数区间,例如:{n},其中n是一个非负数的整数,匹配确定的 n 次。{n,},至少匹配n 次。{n,m},m 和 n 均为非负整数,其中n <= m,最少匹配 n 次且最多匹配 m 次,请注意在逗号和两个数之间不能有空格。
注释:1.正则思维:例如: ^cat:我们一般会理解为:以cat为开头的文本,正则里面的思维是以c为开头的第一个字符,紧接着是a,再紧接着是t的文本
2.正则里面:^是脱字符号,它和美元符号匹配的是一个位置,而不是具体的文本。
3.例如,'z|food' 能匹配 "z" 或 "food"。'(z|f)ood' 则匹配 "zood" 或 "food"。
4.问号,加号,星号。这三个元字符统称为“量词”,因为他们限定了所作用元素的匹配次数
五、结构体
[]:容许使用者在某处列出期望匹配显示的字符,通常又被称为字符组
1.“特殊元字符”:在字符组内部“-”连字符是元字符,可表示一个范围。例如:[1-5] 和[12345]
2.无序:[0-9a-zA-Z] 和 [A-Z0-9a-z]
结构体内可以随意的搭配:[a-z0-9_!.?],匹配一个数字或小写字母,或者下划线,感叹号,逗号,问号
排除型字符组:[^.]:内容会列出所有没有列出的字符,例如:[^1-6],匹配除了1到6之外的所有字符。(这个条件必须是^在结构体的第一个[之后才是这个含义)(匹配一个没有列出的字符不表示可以没有字符)
注释:1。例子:比如我们期望搜索一个单词grey,但是又不知道是否是gray,那么我们就可以写“gr[ea]y”,通过|这个元字符,我们也可以写成grey|gray,进一步简化可以写为gr(e|a)y,用括号来划定多选结构的范围,多选结构可以包含很多字符,到那时不能超过括号的界限。
2。问题:结构体(字符组)和多选结构的区别?
一个字符组只能匹配目标文本中的单个字符,而每个多选结构自身都可能是完整的正则表达式,都可以匹配任意长度的文本。
3.示例一些程序员的正则:(变量名:[a-zA-Z_][a-zA-Z0-9]*,我们常用的声明一个变量)(字符串:”[^”]”)
六、转义字符
1.我们在使用的时候有的时候会遇到网络的点号来匹配网络,针对于元字符,使用反斜杠来转义,不过这样在字符组内部是无效的。例如:\.,\?, \|, \\, \[,\*等
2.如果反斜杠后面跟的不是元字符,反斜杠的意义就根据程序的版本而定,例如某些版本中的\<,\>,\1的处理意义。
七、预查匹配
(pattern) : 匹配 pattern 并获取这一匹配,所获取的匹配可以从产生的 Matches 集合得到。
(?:pattern) :匹配 pattern 但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。
(?=pattern) :正向预查,在任何匹配 pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。
(?!pattern)正向否定预查(negative assert),在任何不匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。
(?<=pattern)反向(look behind)肯定预查,与正向肯定预查类似,只是方向相反。
(?
注释:上面的内容举例说明:
测试的文本:
Swift2 Swift3
1、正向预查
(?:pattern) 匹配结果。Swift(?:2|3)等效于Swift2|Swift3,结果Swift2 Swift3
(?=pattern) 肯定。Swift(?=2),匹配后面跟着2前面的Swift,即第一个Swift,结果Swift
(?!pattern) 否定。Swift(?!2),匹配后面不跟着2前面的Swift,即第二个Swift,结果Swift
2、反向预查
(?<=pattern) 反向肯定预查,与正向肯定预查类似,只是方向相反。
例如,“(?<=95|98|NT|2000)Windows”能匹配“2000Windows”中的“Windows”,
但不能匹配“3.1Windows”中的“Windows”。
(?<!pattern) 反向否定预查,与正向否定预查类似,只是方向相反。
例如“(?<!95|98|NT|2000)Windows”能匹配“3.1Windows”中的“Windows”,
但不能匹配“2000Windows”中的“Windows”。
八、特殊含义字符
\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 匹配版权符号 (?)。
九、特殊
1.在某些支持反向引用的工具中,括号能够记忆其中的子表达式匹配的文本,当然我们在一个表达式中可能见到多个括号,那么我们就可以使用\1,\2,\3来分别表示第一个,第二个,第三个括号内的匹配的文本,
例如: ([a-z]+)+\1表示:一个或多个字符的内容(一个或多个空格)后面的内容相同匹配成功。例如:ww www 等
([a-z])([0-9])\1\2其中\1就表示第一个括号匹配到的对象,\2就表示第二个括号匹配到的对象。例如:w3w3 p5p5 等
十、一些常用的正则案例
1.只能输入汉字:”^[\u4e00-\u9fa5]{0,}$"
2.匹配HTML标记的正则表达式:<(\S*?)[^>]*>.*?>|<.*? />
3.验证身份证号(15位):”\d{14}[[0-9],0-9xX]",(18位):"\d{17}(\d|X|x)";
4.只能输入由26个英文字母组成的字符串:”^[A-Za-z]+$” (限制大写,小写)
5.只能输入非零的正整数:”^\+?[1-9][0-9]*$"
6.只能输入非零的负整数:”^\-[1-9][0-9]*$"
7.匹配浮点数 : ^(-?\d+)(\.\d+)?$
8.匹配整数: ^-?\d+$
9.匹配正浮点数: ^(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*))$
10.匹配非正浮点数(负浮点数 + 0): ^((-\d+(\.\d+)?)|(0+(\.0+)?))$
11.匹配负浮点数: ^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$
12. 12小时制匹配:(1[012]|[1-9]):[0-5][0-9].(am|pm)
13.24小时制,不足前面补0,例如:09:59 :(1[0-9]|2[0-3]|0[0-9]):[0-5][0-9]
时代在变,有些东西不要死记,需要根据实际的情景来自定义来辨别真伪。
题外话
egrep工具:(例如:egrep -i ‘^(From|Subject|Date):’ mailbox)
-i :用来不强制区分大小写
/<:单词的起始位置 (相当于正则的^)(某些版本的egrep可能不支持)
/>:单词的结束位置(相当于正则的$)(某些版本的egrep可能不支持)
最后同事发来一个贺电
结束的时候同事来了一个正则让解析一下。拿出来分享一下:(?!\d+$)(?![a-zA-Z]+$)(?![a-z0-9]+$)(?![A-Z0-9]+$)[a-zA-Z0-9]{6,18}$
解析:(?![a-zA-Z]+$) 断言 此位置 后,字符串结尾之前,所有的字符不能全部由字母组成.
上面的意思就是:由字母(不分大小写)加数字最少6位最大18位。所有字母不能有纯数字,不能由纯字母,不能由单纯的大写或者小写字母加数字组成。
意思就是,必须大小写加数字都有。的6到18位。
圆满结束!后续待补充。