正则表达式(经典讲解)、正则表达式、正则表达式学习、正则学习了解、正则经验

吐槽、建议、解惑入口网址

正则表达式 regular expression(经典讲解)

说明:所谓经典讲解,是作者个人直观感觉。个人以前看过大量“正则表达式”的博客文章,但是始终未能深刻理解正则表达式。但是最近一篇有关linux的书籍(开源旅行手册)提到正则表达式,让我醍醐灌顶。

说明:本文内容主要来自《OS WORLD TRIP》又名《开源世界旅行手册》,由Kardinal著。该书的源地址:下载地址 。正则表达式的内容在部分2:地理的第26章

讲解

正则表达式
简介

  对于文本内容的处理,通常使用交互方式,手工调整;但如果你对源文本比较了解,则可以采用自动化的批量处理方式,这种方式效率高、迅速快批量处理,要求根据一定规则,匹配源文本中的字符,转换为目标文本,这就要用到正则表达式。
最简单的例子,使用 regular 进行匹配,结果如下:

1
`regular` expression

正则表达式有许多变种:glob 表达式、基本正则表达式、perl 正则表达式、emacs 正则表达式……

运算优先级

  正则表达式与数学表达式的不同在于,数学表达式执行数学运算,而正则表达式执行字符运算;相同的是,它们都按一定的优先级进行运算

运算符 操作
\ 转义符
() 捕获、匹配、断言
[] 字符类
*+? 限定符
{} 范围
^$ 位置和顺序
|
转义符

  如果源文本中出现了正则表达式中的运算符,如 ( ,使用 ( 无法匹配下列文本中的括弧,这时要使用 \ 进行转义。用 \( 匹配。在 Emacs 和 Vim 正则表达式中正好反过来,使用 \( 表示分组,用 ( 匹配字符。

1
`(`regular expression)

  在文本中匹配“运算优先级”一节中的所有运算符,都要用这种形式:

1
2
\运算符
如 \* \+ \- \\的使用

在文本中匹配 \ 本身,要用 \\
非运算符前使用 \,则有特殊的意义,例如 \n 匹配一个换行符。常用转义字符:

常规匹配
转义字符 涵义
. 匹配除换行符以外的任意字符
\w 匹配字母或数字或下划线或汉字
\s 匹配任意的空白符
\d 匹配数字
\b 匹配单词的开始或结束,在字符类里代表退格
^ 匹配字符串的开始,在字符类里表 示”非“
$ 匹配字符串的结束
[aeiou] 字符集合,匹配所包含的任意一个字符
反向匹配
转义字符 涵义
\W 匹配任意不是字母,数字,下划线,汉字的字符
\S 匹配任意不是空白符的字符
\D 匹配任意非数字的字符
\B 匹配不是单词开头或结束的位置
[^aeiou] 匹配除了aeiou 这几个字母以外的任意字符
特殊字符
转义字符 涵义
\a 报警字符(打印它的效果是电脑嘀一 声)
\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 当前搜索的开头
字符类

  要想匹配数字、字母、空白很容易,因为已经有了对应这些字符集合的转义符,但是如果你 想匹配没有预定义的字符集合(比如元音字母 a、e、i、o、u),应该怎么办?
  正则表达式中允许你自定义字符类,在方括号里列出它们就可以了

1
[aeiou]

  预定义的字符集合,也可以用字符类表示,如 \d 等价于 [0-9] 有些运算符,在字符类中使用会有另一种意义,例如 ^ 表示“字符串开始”,但在字符类中却
表示 “非”,以 expression 为例,使用 [exp] 匹配:

1
`exp`r`e`ssion

使用 [^exp] 匹配(字符串中非 e、x、p 的字符):

1
exp`r`e`ssion`

而使用 ^[exp] 匹配(以 e、x 或 p 起始的字符串):

1
`e`xpression
限定符

  在上一小节中的表格中,我们知道. 可以匹配除换行符以外的任意字符,使用. 匹配下列 文本:

1
expression

但是 . 每次只匹配一个字符,如果想一次匹配多个,则要使用限定符。

贪婪限定符 惰性限定符 作用
* *? 匹配零次或多次
+ +? 匹配一次或多次
? ?? 匹配零次或一次
{3} {3}? 匹配三次
{3,5} {3,5}? 匹配三到五次
{3,} {3,}? 匹配三次或以上

下面通过实例了解限定符的区别。 es 的匹配结果

1
expr`es`sion

es+ 的匹配结果(e,一个或多个 s)

1
expr`ess`ion

es* 的匹配结果(e,零或多个 s)

1
`e`xpr`ess`ion

es? 的匹配结果(e,零或一个 s)

1
`e`xpr`es`sion

贪婪与懒惰
  使用限定符进行匹配时,默认匹配尽可能多的字符。无论用 .* 还是 .+匹配下列文本,都会匹配全部

1
`expression`

这种方式称为“贪婪模式”。在限定符之后加 ? 则匹配尽可能少的字符,称为“懒惰模式”。 (懒惰模式的详解:.+ 匹配一个或多个任意字符,在贪婪模式中,它匹配尽可能多的字符;而懒惰模式中(.+?),则只匹配一个字符; .{3,5} 在贪婪模式中尽可能匹配5个字符,在懒惰模 式中(.{3,5}?)只匹配3个字符; ?* 这样可以匹配零次的限定符,在懒惰模式下不匹配任何字符( .*?.?? ))。
例如,使用贪婪模式 a.+b 匹配:

1
`aaabab`

使用懒惰模式 a.+?b 匹配:

1
`aaab`ab
分支条件

| 表示“或”,使用它进行分支选择:
例如 [a-z]+|\d+ 匹配单词或数字:

1
expression 123
分组、捕获、不捕获
分组

  使用(表达式)对表达式进行分组,即用小括号来指定子表达式叫做分组。例如使用 (\d{3}\.){2} 匹配下面例子中的数字:

1
abc`123.456.`def

\d{3} 表示三个数字, (\d{3}\.) 表示三个数字加“ . ”为一组,{2} 表示这一组内容重复两次

捕获

  在对表达式进行分组的时候,会捕获文本到自动命名的组里。可以使用 \1 \2 ...... 来反向引用组。例如用 ([a-z]*)\s(\d*) 匹配下列文本, ([a-z]*)\1 组, (\d*)\2

1
`kardinal 1234567`

使用 \2\s\1 替换 ([a-z]*)\s(\d*) ,可以改变两个字符串的顺序

1
1234567 kardinal

使用([a-z]*)\s(\d*)\1匹配下列文本,([a-z]*)\1 组, (\d*)\2组,([a-z]*)\s(\d*)\1\1是反向引用组:

1
`kardinal 1234567kardinal`

如果分组较多,计数可能会不太方便,可以给分组指定名称,例如:

1
2
(?<name>[a-z]*)\s(?<num>\d*)  name、num 是分组的名称
\k<num>\s\k<name> (?#使用“`\k<name>`”反向引用)

使用 (?:表达式) ,则只是分组,而不捕获,下面例子中, (\d*)\1

1
(?:[a-z]*)\s(\d*)

不捕获的典型应用:
用使用在或的应用中,使用或需要使用括号即捕获组,但是又不想存在捕获组,则可以使用不捕获。

1
l(?:i|o|e)ve

注明:反向引用提供了标识字符串中的重复字符或子字符串的方便途径。 例如,如果输入字符串包含某任意子字符串的多个匹配项,可以使用捕获组匹配第一个出现的子字符串,然后使用反向引用匹配后面出现的子字符串。捕获文本放入对应的分组是需要消耗内存的。如何后续不反向引用分组,可以采用捕获但是不分组。

零宽断言

  目前为止,我们学到的正则表达式匹配,都是有“宽度”的,使用 \w+。 匹配下面文本,会将 一同匹配:

1
2
regular。
expression。

如果不想匹配符号,只匹配一个位置,就要用到“零宽断言”(匹配宽度为零,满足一定的条件的断言),零宽断言使用 (?=表达式) 的语法,例如 \w+(?=。) ,其中 (?=。) 表示 前面的位置(先行断言)。

1
2
`regular`。
`expression`。

高级应用(关于为什么叫零宽断言):
表达式写法:\w+(?=。)。\w+ | \w+(?=。)\w+

1
`regular。zero`

如果需要匹配后面的位置,如:

1
2
。`regular`
。`expression`

则要用到后发断言 (?<=。) ,使用 (?<=。)\w+ 得到上面的匹配结果
使用 (?<=<b>).*(?=</b>) 匹配标签中的内容

1
<b>`粗体`</b>
负向零宽断言

  负向零宽断言 (?!表达式) 也是匹配一个零宽度的位置,不过这个位置的“断言”取表达式的反值,例如 (?!表达式) 表示 表达式 前面的位置,如果 表达式 不成立,匹配这个位置;如果 表达式 成立,则不匹配:

1
2
3
4
`expression`
expression。
`expression`,
`expression`;

以上为使用 .+n(?!。) 的匹配结果。注意与 .+n[^。] 匹配的区别

1
2
3
4
expression
`expression,`
`expression;`
expression。

同样,负向零宽断言也有“先行”和“后发”两种,负向零宽后发断言为 (?<!表达式) 使用 (?<![</])para(?!>) 匹配下面文本:

1
<para>`para`表示一个段落</para>

(?<![</]) 表示 para 左边不能为 </ ; (?!>) 表示 para 右边不能为 >

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

Unicode Regular Expressions

\p{script=Han} 识别汉字
\p{javaWhitespace} 匹配空字符

推荐其他网址

正则表达式全集
正则表达式简明参考 (重点在分组和反向引用)
正则表达式30分钟教程 (常用分组语法的总结)
正则表达式的先行断言(lookahead)和后行断言(lookbehind)

不曾拥有,所以努力。(坚持原创技术分享,您的支持将鼓励我继续创作!)