This theme is built with a|

也曾遇见山海

园龄:2年10个月粉丝:0关注:0

正则表达式

这一部分是正则表达式的通用语法,和Python实现无关。

正则表达式本质上只做一件事,那就是编写一个表达式“字符串”,然后用这个字符串去匹配目标文本。核心的核心,都在编写这个“字符串”表达式上面。

注意:文中讨论的所有字符都是英文半角字符,和中文字符没有一毛钱关系!千万不要写成中文标点符号。

一、普通字符

字母、数字、汉字、下划线、以及没有特殊定义的符号,都是"普通字符"。正则表达式中的普通字符,在匹配的时候,只匹配与自身相同的一个字符。

例如:表达式c,在匹配字符串abcde时,匹配结果是:成功;匹配到的内容是c;匹配到的位置开始于2,结束于3。(注:下标从0开始还是从1开始,因当前编程语言的不同而可能不同)

二、元字符

正则表达式中使用了很多元字符,用来表示一些特殊的含义或功能。

表达式匹配
. 小数点可以匹配除了换行符\n以外的任意一个字符
| 逻辑或操作符
[] 匹配字符集中的一个字符
[^] 对字符集求反,也就是上面的反操作。尖号必须在方括号里的最前面
- 定义[]里的一个字符区间,例如[a-z]
\ 对紧跟其后的一个字符进行转义
() 对表达式进行分组,将圆括号内的内容当做一个整体,并获得匹配的值

例如:

a.c匹配abc

(a|b)c匹配acbc

[abc]1匹配a1或者b1或者c1

使用方括号[]包含一系列字符,能够匹配其中任意一个字符。用[^]包含一系列字符,则能够匹配其中字符之外的任意一个字符。

[ab5@]匹配ab5@

[^abc]匹配a,b,c之外的任意一个字符

[f-k]匹配f~k 之间的任意一个字母

[^A-F0-3]匹配A~F以及0~3之外的任意一个字符

三、转义字符

一些无法书写或者具有特殊功能的字符,采用在前面加斜杠"\"进行转义的方法。例如下表所示:

表达式匹配
\r\n 匹配回车和换行符
\t 匹配制表符
\\ 匹配斜杠\
\^ 匹配^符号
\$ 匹配$符号
\. 匹配小数点.

尚未列出的还有问号?、星号*和括号等其他的符号。所有正则表达式中具有特殊含义的字符在匹配自身的时候,都要使用斜杠进行转义。这些转义字符的匹配方法与普通字符类似,也是匹配与之相同的一个字符。

例如表达式\$d,在匹配字符串"abc$de"时,匹配结果是:成功;匹配到的内容是$d;匹配到的位置开始于3,结束于5。

四、预定义匹配字符集

正则表达式中的一些表示方法,可以同时匹配某个预定义字符集中的任意一个字符。比如,表达式\d可以匹配任意一个数字。虽然可以匹配其中任意字符,但是只能是一个,不是多个。如下表所示,注意大小写:

表达式匹配
\d 任意一个数字,0~9 中的任意一个
\w 任意一个字母或数字或下划线,也就是 A~Z,a~z,0~9,_ 中的任意一个
\s 空格、制表符、换页符等空白字符的其中任意一个
\D \d的反集,也就是非数字的任意一个字符,等同于[^\d]
\W \w的反集,也就是[^\w]
\S \s的反集,也就是[^\s]

例如表达式\d\d,在匹配abc123时,匹配的结果是:成功;匹配到的内容是12;匹配到的位置开始于3,结束于5。

五、重复匹配

前面的表达式,无论是只能匹配一种字符的表达式,还是可以匹配多种字符其中任意一个的表达式,都只能匹配一次。但是有时候我们需要对某个片段进行重复匹配,例如手机号码13666666666,一般的新手可能会写成\d\d\d\d\d\d\d\d\d\d\d(注意,这不是一个恰当的表达式),不但写着费劲,看着也累,还不一定准确恰当。

这种情况可以使用表达式再加上修饰匹配次数的特殊符号{},不用重复书写表达式就可以重复匹配。比如[abcd][abcd]可以写成[abcd]{2}

表达式匹配
{n} 表达式重复n次,比如\d{2}相当于\d\d,a{3}相当于aaa
{m,n} 表达式至少重复m次,最多重复n次。比如ab{1,3}可以匹配ababbabbb
{m,} 表达式至少重复m次,比如\w\d{2,}可以匹配a12,_1111,M123等等
? 匹配表达式0次或者1次,相当于{0,1},比如a[cd]?可以匹配a,ac,ad
+ 表达式至少出现1次,相当于{1,},比如a+b可以匹配ab,aab,aaab等等
* 表达式出现0次到任意次,相当于{0,},比如\^*b可以匹配b,^^^b等等

其中有些例子一定要注意!比如ab{1,3}中重复的是b而不是ab(ab){1,3}这样重复的才是ab。表达式\^*b中重复的是\^而不是^,要从左往右读正则表达式,转义符号有更高的优先级,需要和后面的字符整体认读。

表达式\d+\.?\d*在匹配It costs $12.5时,匹配的结果是:成功;匹配到的内容是12.5;匹配到的位置开始于10,结束于14。

表达式go{2,8}gle在匹配Ads by goooooogle时,匹配的结果是:成功;匹配到的内容是goooooogle;匹配到的位置开始于7,结束于17。

六、位置匹配

有时候,我们对匹配出现的位置有要求,比如开头、结尾、单词之间等等。

表达式匹配
^ 在字符串开始的地方匹配,符号本身不匹配任何字符
$ 在字符串结束的地方匹配,符号本身不匹配任何字符
\b 匹配一个单词边界,也就是单词和空格之间的位置,符号本身不匹配任何字符
\B 匹配非单词边界,即左右两边都是\w范围或者左右两边都不是\w范围时的字符缝隙

例如表达式^aaa在匹配xxx aaa xxx时,匹配结果是:失败。因为^要求在字符串开始的地方匹配。

表达式aaa$在匹配xxx aaa xxx时,匹配结果是:失败。因为$要求在字符串结束的地方匹配。

表达式.\b.在匹配@@@abc时,匹配结果是:成功;匹配到的内容是@a;匹配到的位置开始于2,结束于4。

表达式\bend\b在匹配weekend,endfor,end时,匹配结果是:成功;匹配到的内容是end;匹配到的位置开始于15,结束于18。

七、贪婪与非贪婪模式

在重复匹配时,正则表达式默认总是尽可能多的匹配,这被称为贪婪模式。比如,针对文本dxxxdxxxd,表达式(d)(\w+)(d)中的\w+将匹配第一个d和最后一个d之间的所有字符xxxdxxx。可见,\w+在匹配的时候,总是尽可能多的匹配符合它规则的字符。同理,带有?*{m,n}的重复匹配表达式都是尽可能地多匹配。

但是有时候,这种模式不是我们想要的结果,比如最常见的HTML标签匹配。假设有如下的字符串:

1 <table>
2     <tr>
3         <td>苹果</td>
4         <td>桃子</td>
5         <td>香蕉</td>
6     </tr>
7 </table>

我们的意图是获取每个<td></td>标签中的元素内容,那么如果你将正则表达式写成<td>(.*)</td>的话,你得到的是<td>苹果</td><td>桃子</td><td>香蕉</td>这么个东西,而不是“苹果”、“桃子”、“香蕉”。

那么怎么办呢?使用非贪婪模式!

在修饰匹配次数的特殊符号后再加上一个?问号,则可以使匹配次数不定的表达式尽可能少的匹配,使可匹配可不匹配的表达式,尽可能的"不匹配"。如果少匹配就会导致整个表达式匹配失败的时候,与贪婪模式类似,非贪婪模式会最小限度的再多匹配一些,以使整个表达式匹配成功。

表达式<td>(.*?)</td>匹配上面的字符串时,将只得到<td>苹果</td>,再次匹配下一个时,可以得到<td>桃子</td>,以此类推。

针对文本"dxxxdxxxd"举例:

表达式(d)(\w+?)中的\w+?将尽可能少的匹配第一个d之后的字符,结果是只匹配了一个"x",整体只匹配了dx

表达式(d)(\w+?)(d)为了让整个表达式匹配成功,\w+?不得不匹配xxx才可以让后边的d匹配,从而使整个表达式匹配成功。因此,结果是\w+?匹配了xxx,整体匹配了dxxx

八、反向引用

表达式在匹配时,表达式引擎会将小括号()包含的表达式所匹配到的字符串记录下来。在获取匹配结果的时候,小括号包含的表达式所匹配到的字符串可以单独获取。这是一个非常有用也非常重要的特性。在实际应用场合中,当用某种边界来查找,而所要获取的内容又不包含边界时,必须使用小括号来指定所要的范围。比如前面的 <td>(.*?)</td>"

其实,"小括号包含的表达式所匹配到的字符串"不仅是在匹配结束后才可以使用,在匹配过程中也可以使用。表达式后边的部分,可以引用前面"括号内的子匹配已经匹配到的字符串"。引用方法是\加上一个数字。\1引用第1对括号内匹配到的字符串,\2 引用第2对括号内匹配到的字符串……以此类推,如果一对括号内包含另一对括号,则外层的括号先排序号。换句话说,哪一对的左括号"("在前,那这一对就先排序号。举例如下:

表达式('|")(.*?)(\1)在匹配'Hello', "World"时,匹配结果是:成功;匹配到的内容是'Hello'。再次匹配下一个时,可以匹配到 "World"。这里的(\1),动态的引用了('|")匹配到的结果。

表达式(\w)\1{4,}在匹配aa bbbb abcdefg ccccc 111121111 999999999时,匹配结果是:成功;匹配到的内容是ccccc。再次匹配下一个时,将得到999999999。这个表达式要求\w范围的字符至少重复5次,注意与\w{5,}之间的区别。

表达式<(\w+)\s*(\w+(=('|").*?\4)?\s*)*>.*?</\1>在匹配<td id='td1' style="bgcolor:white"></td>时,匹配结果是成功。如果<td></td>不配对,则会匹配失败;如果改成其他配对,也可以匹配成功。这就是常用的HTML标签匹配方法。

九、常用正则表达式

下面是网络上收集的一些常用正则表达式,请参考使用。 PS:各位在复制粘贴的时候务必要小心前后多余的空格!

校验数字的相关表达式:

功能表达式
数字 ^[0-9]*$
n位的数字 ^\d{n}$
至少n位的数字 ^\d{n,}$
m-n位的数字 ^\d{m,n}$
零和非零开头的数字 ^(0|[1-9][0-9]*)$
非零开头的最多带两位小数的数字 ^([1-9][0-9]*)+(.[0-9]{1,2})?$
带1-2位小数的正数或负数 ^(\-)?\d+(\.\d{1,2})?$
正数、负数、和小数 ^(\-|\+)?\d+(\.\d+)?$
有两位小数的正实数 ^[0-9]+(.[0-9]{2})?$
有1~3位小数的正实数 ^[0-9]+(.[0-9]{1,3})?$
非零的正整数 ^[1-9]\d*$
非零的负整数 ^-[1-9]\d*$
非负整数 ^\d+$
非正整数 ^-[1-9]\d*|0$
非负浮点数 ^\d+(\.\d+)?$
非正浮点数 ^((-\d+(\.\d+)?)|(0+(\.0+)?))$
正浮点数 ^[1-9]\d*\.\d*|0\.\d*[1-9]\d*$
负浮点数 ^-([1-9]\d*\.\d*|0\.\d*[1-9]\d*)$
浮点数 ^(-?\d+)(\.\d+)?$

校验字符的相关表达式:

功能表达式
汉字 ^[\u4e00-\u9fa5]{0,}$
英文和数字 ^[A-Za-z0-9]+$
长度为3-20的所有字符 ^.{3,20}$
由26个英文字母组成的字符串 ^[A-Za-z]+$
由26个大写英文字母组成的字符串 ^[A-Z]+$
由26个小写英文字母组成的字符串 ^[a-z]+$
由数字和26个英文字母组成的字符串 ^[A-Za-z0-9]+$
由数字、26个英文字母或者下划线组成的字符串 ^\w+$
中文、英文、数字包括下划线 ^[\u4E00-\u9FA5A-Za-z0-9_]+$
中文、英文、数字但不包括下划线等符号 ^[\u4E00-\u9FA5A-Za-z0-9]+$
可以输入含有^%&’,;=?$\”等字符 [^%&’,;=?$\x22]+
禁止输入含有~的字符 [^~\x22]+

特殊场景的表达式:

功能表达式
Email地址 ^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$
域名 [a-zA-Z0-9][-a-zA-Z0-9]{0,62}(/.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+/.?
InternetURL [a-zA-z]+://[^\s]* 或 ^http://([\w-]+\.)+[\w-]+(/[\w-./?%&=]*)?$
手机号码 ^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\d{8}$
国内电话号码 \d{3}-\d{8}|\d{4}-\d{7}(0511-4405222、021-87888822)
身份证号 ^\d{15}|\d{18}$(15位、18位数字)
短身份证号码 ^([0-9]){7,18}(x|X)?$ 或 ^\d{8,18}|[0-9x]{8,18}|[0-9X]{8,18}?$(数字、字母x结尾)
帐号是否合法 ^[a-zA-Z][a-zA-Z0-9_]{4,15}$(字母开头,允许5-16字节,允许字母数字下划线)
密码 ^[a-zA-Z]\w{5,17}$(以字母开头,长度在6~18之间,只能包含字母、数字和下划线)
强密码 ^(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,10}$(必须包含大小写字母和数字的组合,不能使用特殊字符,长度在8-10之间)
日期格式 ^\d{4}-\d{1,2}-\d{1,2}
一年的12个月(01~09和1~12) ^(0?[1-9]|1[0-2])$
一个月的31天(01~09和1~31) ^((0?[1-9])|((1|2)[0-9])|30|31)$
xml文件 ^([a-zA-Z]+-?)+[a-zA-Z0-9]+\\.[x|X][m|M][l|L]$
双字节字符 [^\x00-\xff](包括汉字在内,可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1))
空白行的正则表达式 \n\s*\r (可以用来删除空白行)
HTML标记的正则表达式 <(\S*?)[^>]*>.*?</\1>|<.*? />(对于复杂的嵌套标记依旧无能为力)
首尾空白字符的正则表达式 ^\s*|\s*$或(^\s*)|(\s*$)(可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等))
腾讯QQ号 [1-9][0-9]{4,} (腾讯QQ号从10000开始)
中国邮政编码 [1-9]\d{5}(?!\d) (中国邮政编码为6位数字)
IP地址提取 \d+\.\d+\.\d+\.\d+ (提取IP地址时有用)
IP地址合法性判断 ((?:(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d?\\d))

十、总结

关于正则表达式还有更多的高级用法和特性,不过对于这部分内容,各个不同的正则引擎在细节上有点不同,因此就不深入介绍了,有需要的同学,可以自行查看对应版本的说明文档。

本文作者:也曾遇见山海

本文链接:https://www.cnblogs.com/laochai/p/16216610.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   也曾遇见山海  阅读(58)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 Secret 茶太
Secret - 茶太
00:00 / 00:00
An audio error has occurred.

作词 : 茶太

作曲 : Bassy

じゃあねと軽(かる)く手(て)を振(ふ)る 肩越(かたご)しに君(きみ)を見(み)る

改札(かいさつ)吸(す)い込(こ)んだ切符(きっぷ)つまむ 歩(ある)き出(だ)す

今日(きょう)も一日(いちにち)ずっと我慢(がまん)してた

この気持(きも)ち抑(おさ)える自信(じしん)が無(な)い

ほんとはねもっとそばに居(い)て ずっと君(きみ)と一緒(いっしょ)に居(い)たい

でもそんなこと言(い)えないから ため息(いき)混(ま)じりの秘密(ひみつ)

一歩(いっぽ)分(ぶん)近(ちか)づいたなら その分(ぶん)壁(かべ)を作(つく)る

臆病(おくびょう)者(もの)だから 気付(きづ)かないでお願(ねが)いと

この距離(きょり)が居心地(いごこち)良(よ)すぎるから

壊(こわ)れないようにと祈(いの)ってみる

ほんとはねもっとそばに居(い)て 君(きみ)を独(ひと)り占(じ)めしたいんだ

でもそんなこと言(い)えないから ため息(いき)混(ま)じりの秘密(ひみつ)

日(ひ)を追(お)って膨(ふく)らんでく気持(きも)ち

笑顔(えがお)の下(した)隠(かく)しちゃうんだいつも

振(ふ)り向(む)いた先(さき)にいる君(きみ)が 無邪気(むじゃき)に笑(わら)うから愛(いと)しくて

人込(ひとご)みの中(なか)で手(て)を振(ふ)った また明日(あした)ねと笑(わら)いながら

ほんとはもっとそばに居(い)て ずっと君(きみ)と一緒(いっしょ)に居(い)たい

でもそんなこと言(い)えないから ため息(いき)混(ま)じりの秘密(ひみつ)

終わり