正则表达式 - 2018-08-07

2018-08-08  本文已影响31人  默写年华Antifragile

参考:正则表达式30分钟入门教程

元字符 metacharacter

  1. \b : 匹配单词的开始或结尾,只匹配一个位置
  2. . (点) 匹配除换行符以外的任何字符
  3. \d 匹配数字 0-9
  4. \s 匹配任意的空白符,包括空格、制表符、换行符、中文全角空格等
  5. \w 匹配字母、数字、下划线
  6. ^ 匹配字符串的开始
  7. $匹配字符串的结束,比如 一个网站要求你填写的QQ号必须为5-12位数字时,可以使用^\d{5,12}$,单独的{2}表示不多不少重复2次,{5,12}表示重复的次数不能少于5次,不能多余12次。
    如果选中了处理多行的选项,^和$的意义为 匹配行的开始和结束处
  8. () 也是元字符,用来分组
  9. 如果要查找元字符本身,比如 . 和 * ,就需要使用反斜杠 \ 来进行转义,查找反斜杠 \,也得用 \\

限定符 指定数量的代码

代码 说明
* 重复 0 次或者重复多次
+ 重复 1 次或者重复多次
? 重复 0 次或者 1 次
{n} 重复了 n 次
{ n, } 重复了 n 次或者更多次
{m,n} 重复 m 到 n次

字符类

使用中括号 []

  1. [aeiou] 匹配任何一个元音字母
  2. [.?!]匹配标点符号 . ? !
  3. [0-9] 匹配数字 0-9 与 \d 代表的意义相同
  4. [0-9a-zA-Z]\w等价
  5. \(?0\d{2}[(-]?\d{8} 首先是一个转义字符\( 它出现0次或1次(?), 然后是数字 0,后面跟着 2 个数字 (\d{2}), 然后是空格或者-中的一个,它出现1次或者不出现(?),最后是8个数字(\d{8}) 。上式可以匹配010)88886666,或022-22334455,或02912345678等常见电话号码格式,但是也能匹配 010)12345678或(022-87654321这样的“不正确”的格式,因此就要使用下面的 分支条件

分支条件

|号把不同的规则分隔开
e.g. \(0\d{2}\)[- ]?\d{8}|0\d{2}[- ]?\d{8}这个表达式匹配三位区号的电话号码,其中区号可以用小括号括起来,也可以不用,区号与本地号支架你可以用连字号或空格间隔,也可以不用。
注意: 匹配分支条件时,将会从左到右地测试每个条件,如果满足了某个条件将不会再去管其他的条件
比如:\d{5}-\d{4}|\d{5}用于匹配美国的邮政编码,美国邮编的规则是5位数字或者用连字号间隔的9位数字;如果写成:
\d{5}|\d{5}-\d{4}那么就只会匹配5位的邮编,以及9位邮编的前五位

分组

e.g. 描述一个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? 分为三种情况:

反义

采用大写的方式,与原来的小写方式表达相反的含义

代码 说明
\W \w相对应,匹配不是字母、数字、下划线的字符
\S \s相对应,匹配任何不是空白符的字符
\D \d相对应,匹配任意非数字的字符
\B \b相对应,匹配不是单词开头或结束的位置
[^x] 匹配除x以外的字符
[^aeiou] 匹配除aeiou以外的任意字符

后向引用

使用小括号指定一个子表达式后,匹配这个子表达式的文本(也就是此分组捕获的内容)可以在表达式或其它程序中作进一步的处理。默认情况下,每个分组会自动拥有一个组号,规则是:从左向右,以分组的左括号为标志,第一个出现的分组的组号为1,第二个为2,以此类推。
后向引用用于重复搜索前面某个分组匹配的文本,\1代表分组1匹配的文本
e.g.

代码 说明
(exp) 匹配 exp ,并捕获文本到自动命名的组里
(?<name>exp) 匹配 exp ,并捕获文本到名称为 name 组里,也可以(?'name'exp)
?:exp 匹配 exp ,不捕获匹配的文本,也不给此分组分配组号

零宽断言

e.g. (?<=\s)\d+(?=\s) 匹配以空白字符间隔的数字(不包括这些空白字符)

负向零宽断言

(?!exp)断言此位置的后面不能匹配表达式 exp
e.g.
\d{3}(?!\d)匹配三位数字,而且这三位数字的后面不能是数字
\b((?!abc)\w)+\b匹配不包含连续字符串 abc 的单词
一个更复杂的例子:(?<=<(\w+)>).*(?=<\/\1>)
匹配不包含属性的简单HTML标签内的内容;(?<=<(\w+)>)指定了这样的前缀:被尖括号括起来的单词;然后是.*,即任意字符串,最后是一个后缀?=<\/\1>,这里\/(斜杠和反斜杠)b用到了前面的字符转义;\1用到了反向引用,引用的正是捕获的第一组\w+匹配的内容,这样,如果前缀是<html>, 那后缀就是 </html>;整个表达式匹配的是<html>和</html>之间的内容(不包括前缀和后缀)

注释

(?<=     #断言要匹配的文本的前缀
<(\w+)>  #查找尖括号括起来的字母或数字(即HTML或XML标签)
)             #前缀结束
.*            #匹配任意文本
(?=          #断言要匹配的文本的后缀
<\/\1>       #查找尖括号括起来的内容
)               #后缀结束

贪婪与懒惰

代码 说明
*? 重复任意次,但是尽可能少的重复
+? 重复1次或更多次,但尽可能少的重复
?? 重复0次或1次,但尽可能少重复
{m,n}? 重复 m-n 次,但是尽可能少重复
{n,}? 重复n次以上,但是尽可能少重复

平衡组/递归匹配

有时我们需要匹配像( 100 * ( 50 + 15 ) )这样的可嵌套的层次性结构,这时简单地使用(.+)则只会匹配到最左边的左括号和最右边的右括号之间的内容(这里我们讨论的是贪婪模式,懒惰模式也有下面的问题)。假如原来的字符串里的左括号和右括号出现的次数不相等,比如( 5 / ( 3 + 2 ) ) ),那我们的匹配结果里两者的个数也不会相等。有没有办法在这样的字符串里匹配到最长的,配对的括号之间的内容呢?

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

思路:每碰到左括号,就压入一个 "Open" ,每碰到一个右括号,就弹出一个,到了最后看堆栈是否为空,如果不为空则证明左括号比右括号多,那匹配就应该失败。正则表达式会进行回溯(放弃最前面或最后面的一些字符),尽量使整个班表达式得到匹配

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

        (
            (?'Open'<)  #匹配到了左括号,往堆栈压入一个'Open'
            [^<>]*      #匹配左括号后面不是括号的内容
        )+              #可以出现多次

        (
            (?'-Open'>) #碰到了右括号,则从堆栈弹出一个'Open'
            (^<>)*      #匹配右括号后面不是括号的内容
        )+              #可以出现多次

    )*                  #整个上面可以出现多次

    (?(Open)(?!))       #在遇到最外层的右括号前面,判断堆栈上是否还有"Open",
                        #如果还有,则执行 (?!),由于后面没有后缀表达式,匹配总是失败
>                       #最外层的括号
上一篇 下一篇

猜你喜欢

热点阅读