正则表达式:快速入门

正则表达式:快速入门

在正则表达式中,元字符是一类非常特殊的字符,它能够匹配一个位置或者字符集合中的一个字符

分类:

  • 匹配位置的元字符。
  • 匹配字符的元字符。

说明:

匹配位置的元字符包括三个字符:^(匹配行的开始)、$(行的结束)、\b(匹配单词的边界)

实例:

^String    被匹配的字符串以String开始
String   被匹配的字符串以String结束
^String   被匹配的字符串只包含String
^$    匹配一个空行
   匹配任意行
 \bStr    被匹配的字符串必须以Str开头,且Str之前必须是单词的分界线。
\bString\b    匹配一个完整的单词,String

说明:

匹配字符的元字符包括7个字符:.(点号)、\w、\W、\s、\S、\d、\D.
: .(点号)匹配除换行符之外的任意字符。
:\w      匹配单词字符(包括数字、字母、下划线、汉字等)。
:\W      匹配任意的非单词字符。
:\s        匹配任意的空白字符,如空格、制表符、换行符等
:\S        匹配任意的非空字符
:\d        匹配任意的数字
:\D        匹配任意的非数字字符

实例:

\ba\w\w\w\w\w\w\w\b    匹配一个以字母a开头的长度等于8的任意单词
\W\W    匹配长度为2且不包含任意单词字符
·源文本:
1
a
A
?
***

·正则表达式
\W\W

·结果:
?
*
**

说明:

        非单词字符包括换行符,所以A后面的换行符与?组合成目标串,一次类推。


元字符通常只能匹配一个位置或字符集合中的一个字符。通过字符类我们则可以对要匹配的字符做更详细的约束和限制。
字符类是一个集合,如果该字符集合中的任何一个字符被匹配,则它会找到该匹配项。
字符类是一种迷你语言,在[]中定义。
正则表达式中引入连接符-,来表示范围。
    [0123456]    匹配0-6中的任意一个
    [0123456789]   [0-9]     匹配任何一个数字
    <H[123456]>        匹配HTML中的标题标签
    [Jj]ack                匹配Jack或者jack
    [A-Z][a-z]        匹配任意一个字母             

说明:

    如果字符类中包括-,即-也是字符类的一个元素,那么必须将其作为第一个字符。

    如:[-a]表示匹配字符-或者字符a


    正则表达式定义了一些特殊的元字符,如^、$、.等,由于这些字符在正则表达式中被解释成其他指定的意义,如果需要匹配这些字符,则需要使用字符转义来解决这一问题
    转义字符为:\,它可以取消这些字符在表达式中具有的特殊意义。

特别字符

在使用正则表达式时,如果需要匹配不在字符类指定范围内的字符时,可以使用反义规则。
在字符类中,如果^是字符类的第一个字符,则表示否定该字符类,即匹配除了该字符类以外的所有字符。
    [^-]    匹配除了连接符以外的所有字符
    a[^b]    匹配字符a之后不是字符b的字符串
    \B        匹配不是单词开头或结尾的任意位置

正则表达式的元字符一次只能匹配一个位置或一个字符,如果要匹配0个,1个或多个字符时,需要使用限定符。
限定符用于指定允许特定字符或字符集自身重复出现的次数。
代码/语法说明
* 重复零次或更多次
+ 重复一次或更多次
? 重复零次或一次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次

如果在限定符之后再添加一个字符"?",则表示尽可能少的重复字符?之前的限定符号的重复次数,这种匹配方式称为懒惰匹配。
贪婪匹配,仅仅使用单个限定符*、+、?、{n}、{n,}、{n,m}的匹配。
代码/语法说明
*? 重复任意次,但尽可能少重复
+? 重复1次或更多次,但尽可能少重复
?? 重复0次或1次,但尽可能少重复
{n,m}? 重复n到m次,但尽可能少重复
{n,}? 重复n次以上,但尽可能少重复
a.*b        贪婪匹配:匹配以a开头、以b结尾、最长的字符。
a.*?b        懒惰匹配:匹配以a开头、以b结尾、最短的字符。

如:

·源文本
aabab

·贪婪匹配
aabab
·懒惰匹配
aab
ab
//aab(第一到第三个字符)和ab(第四到第五个字符)
a??b    使用0次重复或一次重复

如果要匹配区号为3位或4位的固定电话号码,可以使用替换满足需求。
最简单的替换使用“|”完成。
0\d{2}-\d{8}|0\d{3}-\d{7}                    这个表达式能匹配两种以连字号分隔的电话号码:一种是三位区号,8位本地号(如010-12345678),一种是4位区号,7位本地号(0376-2233445)。
\(?0\d{2}\)?[- ]?\d{8}|0\d{2}[- ]?\d{8}                            这个表达式匹配3位区号的电话号码,其中区号可以用小括号括起来,也可以不用,区号与本地号间可以用连字号或空格间隔,也可以没有间隔。你可以试试用分枝条件把这个表达式扩展成也支持4位区号的。
\d{5}-\d{4}|\d{5}                                        这个表达式用于匹配美国的邮政编码。美国邮编的规则是5位数字,或者用连字号间隔的9位数字。之所以要给出这个例子是因为它能说明一个问题:使用分枝条件时,要注意各个条件的顺序。如果你把它改成\d{5}|\d{5}-\d{4}的话,那么就只会匹配5位的邮编(以及9位邮编的前5位)。原因是匹配分枝条件时,将会从左到右地测试每个条件,如果满足了某个分枝的话,就不会去再管其它的条件了。

分组又称为子表达式,即把一个正则表达式的全部或部分分出一个或多个组。
分组使用的字符是"()"。
分组之后可以将()之内的表达式看成一个整体。
(\d{1,3}\.){3}\d{1,3}    匹配一个IP地址

\d{1,3}匹配1到3位的数字,(\d{1,3}\.){3}匹配三位数字加上一个英文句号(这个整体也就是这个分组)重复3次,最后再加上一个一到三位的数字(\d{1,3})。

 


当一个正则表达式被分组之后,每一个组会自动被赋予一个组号,该组号可以代表该组的表达式。
分组的编制规则:从左至右,以左括号为标志,第一个分组为1,第二个分组为2,以此类推。
反向引用提供了查找重复字符组的方便方法。它可被认为是再次匹配同一个字符串的快捷指令。
   \b(\w)\1\b        匹配具有两个重复字符的单词

注意:

\b(\w)\1\b != \b\w\w\b

\b(\w)(\d)\1\2\b    匹配一个字符和数字开头并重复该字符和数字的字符串

    分组不但可以使用数字作为组号,也可以使用自定义名称作为组号。

    (?<word>\w+)    将分组后的子表达式命名为word
    (?'word'\w+)       将分组后的子表达式命名为word
表4.常用分组语法
分类代码/语法说明
捕获 (exp) 匹配exp,并捕获文本到自动命名的组里
(?<name>exp) 匹配exp,并捕获文本到名称为name的组里,也可以写成(?'name'exp)
(?:exp) 匹配exp,不捕获匹配的文本,也不给此分组分配组号
零宽断言 (?=exp) 匹配exp前面的位置
(?<=exp) 匹配exp后面的位置
(?!exp) 匹配后面跟的不是exp的位置
(?<!exp) 匹配前面不是exp的位置
注释 (?#comment) 这种类型的分组不对正则表达式的处理产生任何影响,用于提供注释让人阅读

接下来的四个用于查找在某些内容(但并不包括这些内容)之前或之后的东西,也就是说它们像\b,^,$那样用于指定一个位置,这个位置应该满足一定的条件(即断言),因此它们也被称为零宽断言。=
断言用来声明一个应该为真的事实。正则表达式中只有当断言为真时才会继续进行匹配。
  • (?=exp)也叫零宽度正预测先行断言,它断言自身出现的位置的后面能匹配表达式exp
    • 比如\b\w+(?=ing\b),匹配以ing结尾的单词的前面部分(除了ing以外的部分),如查找I'm singing while you're dancing.时,它会匹配sing和danc。
  • (?<=exp)也叫零宽度正回顾后发断言,它断言自身出现的位置的前面能匹配表达式exp
    • 比如(?<=\bre)\w+\b会匹配以re开头的单词的后半部分(除了re以外的部分),例如在查找reading a book时,它匹配ading。

假如你想要给一个很长的数字中每三位间加一个逗号(当然是从右边加起了),你可以这样查找需要在前面和里面添加逗号的部分:((?<=\d)\d{3})+\b,用它对1234567890进行查找时结果是234567890。

下面这个例子同时使用了这两种断言:

(?<=\s)\d+(?=\s)        匹配以空白符间隔的数字(再次强调,不包括这些空白符)。

说明:

    前面我们提到过怎么查找不是某个字符或不在某个字符类里的字符的方法(反义)。

    但是如果我们只是想要确保某个字符没有出现,但并不想去匹配它时怎么办?例如,如果我们想查找这样的单词--它里面出现了字母q,但是q后面跟的不是字母u,我们可以尝试这样:

        \b\w*q[^u]\w*\b匹配包含后面不是字母u的字母q的单词。

    但是如果多做测试(或者你思维足够敏锐,直接就观察出来了),你会发现,如果q出现在单词的结尾的话,像Iraq,Benq,这个表达式就会出错。这是因为[^u]总要匹配一个字符,所以如果q是单词的最后一个字符的话,后面的[^u]将会匹配q后面的单词分隔符(可能是空格,或者是句号或其它的什么),后面的\w*\b将会匹配下一个单词,于是\b\w*q[^u]\w*\b就能匹配整个Iraq fighting。

    负向零宽断言能解决这样的问题,因为它只匹配一个位置,并不消费任何字符。现在,我们可以这样来解决这个问题:\b\w*q(?!u)\w*\b。

表达式(?!expression)又称为负向零宽度断言或者零宽度负预测先行断言,它断言自身位置的后名不能匹配字符串expression。

    零宽度负预测先行断言(?!exp),断言此位置的后面不能匹配表达式exp。

                \d{3}(?!\d)匹配三位数字,而且这三位数字的后面不能是数字;
                \b((?!abc)\w)+\b匹配不包含连续字符串abc的单词。

同理,我们可以用(?<!exp),零宽度负回顾后发断言来断言此位置的前面不能匹配表达式exp:(?<![a-z])\d{7}匹配前面不是小写字母的七位数字。

请详细分析表达式(?<=<(\w+)>).*(?=<\/\1>),这个表达式最能表现零宽断言的真正用途。

一个更复杂的例子:(?<=<(\w+)>).*(?=<\/\1>)匹配不包含属性的简单HTML标签内里的内容。(?<=<(\w+)>)指定了这样的前缀:被尖括号括起来的单词(比如可能是<b>),然后是.*(任意的字符串),最后是一个后缀(?=<\/\1>)。注意后缀里的\/,它用到了前面提过的字符转义;\1则是一个反向引用,引用的正是捕获的第一组,前面的(\w+)匹配的内容,这样如果前缀实际上是<b>的话,后缀就是</b>了。整个表达式匹配的是<b>和</b>之间的内容(再次提醒,不包括前缀和后缀本身)。


posted @ 2017-04-18 15:34  子烁爱学习  阅读(442)  评论(0编辑  收藏  举报