分享

快速回忆正则表达式

 天才白痴书馆 2015-04-16

这不是入门级文章,但如果你对正则表达式有了解,或使用过,则能帮助你快速回忆。阅读此文需要你之前使用过正则表达式或者有些了解,因为我没有写很多的例子。总结正则表达式,只是因为个人在几年的积累之后,想通俗简单的概述正则表达式中各种符号和用法。网上有很多关于正则表达式,但总感觉专业术语太多,读起来拗口或绕弯子。最后两小节来自正则表达式30分钟入门教程,有部分修缮。

正则表达式字符串由两种基本字符组成:原义文本字符和元字符。所谓元字符就是正则表达式中具有特殊意义的专用字符,元字符可能是一个字符,也可能是多个字符组成的一个基本单元。

元字符

一个元字符可能代表的是一个数字、字母、位置或数量。

代表字符

代码

说明

.

匹配除换行符以外的任意字符

\w

匹配字母或数字或下划线或汉字

\s

匹配任意的空白符

\d

匹配数字

\S

与\s相反

\D

与\d相反

\W

与\w相反

 

代表数量

 

代码

说明

*

重复零次或更多次

+

重复一次或更多次

?

重复零次或一次

{n}

重复n次

{n,}

重复n次或更多次

{n,m}

重复n到m次


 

代表位置

代码

说明

\b

匹配单词的开始或结束

\B

不是单词开头或结束的开始

^

匹配字符串的开始

$

匹配字符串的结束

 

转义字符

既然元字符在正则表达式中有特殊意义,如果现在要把它们当成一般原义文本字符,怎么办?在元字符前添加反斜杠\,表示当前元字符已经失去了正则表达式中的特殊意义,变成了一个原义文本字符。

预定义字符集

元字符匹配范围太广,如果只是想匹配某小范围内的字符,该怎么办?

很简单,使用方括号列出来即可。如[aeiou]或[.?!],表示只能匹配方括号中出现的字符。

需要注意的是:在这个方括号中,代斜杠的元字符依然是元字符,但那些没有斜杠前缀的则都不再是元字符,但多了一个元字符,就是连字符-。

 

如果连字符在两个字符之间,那么就表示范围,连字符本身不算,如[0-9], [a-z]等; 如果连字符后面没有字符,则表示连字符也是预定义的字符集的字符之一,如[*%-];

 

在预定义字符集中,可以使用转义字符

 

 

反义(antonymy)

有时只需要查找不属于某个简单定义的字符集的字符,这就是反义

代码

说明

\W

匹配任意不是字母,数字,下划线,汉字的字符

\S

匹配任意不是空白符的字符

\D

匹配任意非数字的字符

\B

匹配不是单词开头或结束的位置

[^x]

匹配除了x以外的任意字符

[^aeiou]

匹配除了aeiou这几个字母以外的任意字符

 

或 | 分支

 

 

这相当于或语句,具体方法是用垂直线|把不同的规则分隔开。如0\d{2}-\d{8}|0\d{3}-\d{7}这个表达式能匹配两种以连字号分隔的电话号码:一种是三位区号,8位本地号(如010-12345678),一种是4位区号,7位本地号(0376-2233445)。

分组(group)

所谓分组,其实就是把小括号()中正则表达式作为一个小的匹配单元。

分组的作用有两个:

 

默认情况下,正则表达式会解析器给每一个小组分配一个组号,通过这种方式,后面的正则表达式就可以通过组号引用这个小组匹配的内容。小组后可以跟代表数量的元字符,简化正则表达式书写;比如,(\d{1,3}\.){3}\d{1,3}是一个简单的IP地址匹配表达式。

 

小组后跟代表数量的元字符有一个问题需要注意:感觉应该是生成了若干个小组,但其实就是一个分组,因为组号是解析器解析的时候给的,代表小组的字符串在整个正则表达式字符串中只出现了一次,所以只分配了一个唯一的组号。因此,正则表达式执行完后,这个组匹配的内容是最后一次匹配的内容。

比如:

/(\d{1,3}\.){3}\d{1,3}/g.exec("201.202.203.204");

结果:["201.202.203.204", "203."]

后面的正则表达式通过组号引用前面分组匹配的内容,这个就叫后向引用。组号的规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,依此类推。引用的时候记得在组号前加上斜杠。

比如:

/\b(\w+)\b\s+\1\b/.test("hellohello"); // true

/\b(\w+)\b\s+\1\b/.test("hellohell"); // false

 

分组的语法:

代码/语法

说明

(exp)

匹配exp,并捕获文本到自动命名的组里

(?exp)

匹配exp,并捕获文本到名称为name的组里,也可以写成(?'name'exp)

(?:exp)

匹配exp,不捕获匹配的文本,也不给此分组分配组号

 

零宽断言

 

零宽断言像\b、^、$一样用于指定一个位置,但这个位置应该满足一定的条件,这个条件就被称之为断言。因为这个断言不像分组一样有组号,不消耗匹配的字符串,所以称之为零宽断言。

代码/语法

说明

(?=exp)

断言这个位置的后面,一定有匹配exp的内容;

(?<=exp)

断言这个位置的前面,一定有匹配exp的内容;

(?!exp)

断言这个位置的后面,一定没有匹配exp的内容

(?

断言这个位置的前面,一定没有匹配exp的内容

注释

注释用(?#comment)表示。

贪婪与懒惰(greedy and idle)

一段固定的正则表达式,它所能匹配的可能是整个字符串,也可能只是整个字符串的一部分。假如出现一段正则表达式既能匹配整个字符串,也能只匹配部分该如何处理?

/h.*o/.exec("hello ho"); // ["hello ho"]

/h.*?o/.exec("helloho"); // ["hello"]

以上这个例子中,匹配hello使用的是懒惰匹配,而没有问号限定的则是使用的贪婪匹配。

 

所谓贪婪匹配就是尽可能多的重复,以匹配尽可能多的字符。反之,懒惰匹配就是匹配尽可能少的字符。默认是贪婪算法,只有在表示数量的元字符后使用了懒惰限定符(也就是问号)才表示这个地方使用懒惰算法。

懒惰限定符

语法

说明

*?

重复任意次,但尽可能少重复

+?

重复1次或更多次,但尽可能少重复

??

重复0次或1次,但尽可能少重复

{n,m}?

重复n到m次,但尽可能少重复

{n,}?

重复n次以上,但尽可能少重复

 

平衡组(balance group)/递归匹配(recursive match)

 

前面讲的所有匹配都是线性的,对于匹配像( 100 * ( 50 + 15 ) )这样的可嵌套的层次性结构,则上面的一切方法都没用。因为你不可能知道括号什么时候出现,而且如果左括号和右括号出现的次数不相等怎么办?如何匹配到最长的,配对的括号之间的内容?

这里需要用到以下的语法构造:

  • (?'group'exp) 把捕获的内容命名为group,并压入堆栈(Stack) (?'-group'exp) 从堆栈上弹出最后压入堆栈的名为group的捕获内容,如果堆栈本来为空,则本分组的匹配失败 (?(group)yes|no) 如果堆栈上存在以名为group的捕获内容的话,继续匹配yes部分的表达式,否则继续匹配no部分 (?!) 零宽负向先行断言,由于没有exp,试图匹配总是失败

    为了避免(和\(把你的大脑彻底搞糊涂,我们还是用尖括号代替圆括号吧。现在我们的问题变成了如何把xx aa> yy这样的字符串里,最长的配对的尖括号内的内容捕获出来?

    < #最外层的左括号

    [^<>]* #最外层的左括号后面的不是括号的内容

    (

    (

    (?'Open'<) #碰到了左括号,在黑板上写一个"Open"

    [^<>]* #匹配左括号后面的不是括号的内容

    )+

    (

    (?'-Open'>) #碰到了右括号,擦掉一个"Open"

    [^<>]* #匹配右括号后面不是括号的内容

    )+

    )*

    (?(Open)(?!)) #在遇到最外层的右括号前面,判断黑板上还有没有没擦掉的"Open";如果还有,则匹配失败

     

    > #最外层的右括号

    更多

    常用转义字符及其他部分元字符

    代码/语法

    说明

    \a

    报警字符(打印它的效果是电脑嘀一声)

    \b

    通常是单词分界位置,但如果在字符类里使用代表退格

    \t

    制表符,Tab

    \r

    回车

    \v

    竖向制表符

    \f

    换页符

    \n

    换行符

    \e

    Escape

    \0nn

    ASCII代码中八进制代码为nn的字符

    \xnn

    ASCII代码中十六进制代码为nn的字符

    \unnnn

    Unicode代码中十六进制代码为nnnn的字符

    \cN

    ASCII控制字符。比如\cC代表Ctrl+C

    \A

    字符串开头(类似^,但不受处理多行选项的影响)

    \Z

    字符串结尾或行尾(不受处理多行选项的影响)

    \z

    字符串结尾(类似$,但不受处理多行选项的影响)

    \G

    当前搜索的开头

    \p{name}

    Unicode中命名为name的字符类,例如\p{IsGreek}

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多