## 第 26 章 正则表达式
**目录**
[](ch26.html#id3119846)
[简介](ch26s02.html)
[运算优先级](ch26s03.html)
[转义符](ch26s04.html)
[字符类](ch26s05.html)
[限定符](ch26s06.html)
[贪婪与懒惰](ch26s06.html#id3120652)
[分支条件](ch26s07.html)
[分组、捕获](ch26s08.html)
[分组](ch26s08.html#id3120847)
[捕获](ch26s08.html#id3120909)
[零宽断言](ch26s09.html)
[负向零宽断言](ch26s09.html#id3121214)
## 简介
对于文本内容的处理,通常使用交互方式,手工调整;但如果你对源文本比较了解,则可以采用自动化的批量处理方式,这种方式效率高、迅速快
批量处理,要求根据一定规则,匹配源文本中的字符,转换为目标文本,这就要用到正则表达式
最简单的例子,使用`regular`进行匹配,结果如下:
```
`regular` expression
```
正则表达式有许多变种:glob 表达式、基本正则表达式、perl 正则表达式、emacs 正则表达式……
如[“通配符”一节](ch14s06.html#glob "通配符")中介绍的为最简单的 glob 表达式
## 运算优先级
正则表达式与数学表达式的不同在于,数学表达式执行数学运算,而正则表达式执行字符运算;相同的是,它们都按一定的优先级进行运算
| 运算符 | 操作 |
| --- | --- |
| \ | 转义符 |
| () | 捕获、匹配、断言 |
| [] | 字符类 |
| *+? | 限定符 |
| {} | 范围 |
| ^$ | 位置和顺序 |
| | | 或 |
## 转义符
如果源文本中出现了正则表达式中的运算符,如`(`,使用 `(` 无法匹配下列文本中的括弧,这时要使用 `\` 进行转义。用 `\(`匹配[[48](ch26s04.html#ftn.id3119995)]:
```
`(`regular expression)
```
在文本中匹配[“运算优先级”一节](ch26s03.html "运算优先级")中的所有运算符,都要用这种形式:
```
\运算符
```
在文本中匹配`\`本身,要用 `\\`
非运算符前使用 `\` ,则有特殊的意义,例如`\n`匹配一个换行符。常用转义字符:
| 转义字符 | 涵义 |
| --- | --- | --- |
| 常规匹配 | . | 匹配除换行符以外的任意字符 |
| \w | 匹配字母或数字或下划线或汉字 |
| \s | 匹配任意的空白符 |
| \d | 匹配数字 |
| \b | 匹配单词的开始或结束,在字符类里代表退格 |
| ^ | 匹配字符串的开始,在字符类里表示”非“ |
| $ | 匹配字符串的结束 |
| 反向匹配 | \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 | 当前搜索的开头 |
* * *
> [[48](ch26s04.html#id3119995)] 在 Emacs 和 Vim 正则表达式中正好反过来,使用`\(`表示分组,用`(`匹配字符
## 字符类
要想匹配数字、字母、空白很容易,因为已经有了对应这些字符集合的转义符,但是如果你想匹配没有预定义的字符集合(比如元音字母 a、e、i、o、u),应该怎么办?
正则表达式中允许你自定义字符类,在方括号里列出它们就可以了
```
[aeiou]
```
预定义的字符集合,也可以用字符类表示,如 `\d` 等价于 `[0-9]`
有些运算符,在字符类中使用会有另一种意义,例如`^`表示“字符串开始”,但在字符类中却表示 “非”,以`expression`为例,使用`[exp]`匹配:
```
`exp`r`e`ssion
```
使用`[^exp]`匹配(字符串中非 e、x、p 的字符):
```
exp`r`e`ssion`
```
而使用`^[exp]`匹配(以 e、x 或 p 起始的字符串):
```
`e`xpression
```
## 限定符
在上一小节中的表格中,我们知道 `.` 可以匹配除换行符以外的任意字符,使用`.`匹配下列文本:
```
expression
```
但是`.`每次只匹配一个字符,如果想一次匹配多个,则要使用限定符
| 限定符 | 作用 |
| --- | --- |
| * | 匹配零次或多次 |
| + | 匹配一次或多次 |
| ? | 匹配零次或一次 |
| {3} | 匹配三次 |
| {3,5} | 匹配三到五次 |
| {3,} | 匹配三次或以上 |
下面通过实例了解限定符的区别。 `es` 的匹配结果
```
expr`es`sion
```
`es+` 的匹配结果(e,一个或多个 s)
```
expr`ess`ion
```
`es*` 的匹配结果(e,零或多个 s)
```
`e`xpr`ess`ion
```
`es?` 的匹配结果(e,零或一个 s)
```
`e`xpr`es`sion
```
### 贪婪与懒惰
使用限定符进行匹配时,默认匹配尽可能多的字符。无论用 `.*` 还是 `.+` 匹配下列文本,都会匹配全部
```
`expression`
```
这种方式称为“贪婪模式”。在限定符之后加 `?` 则匹配尽可能少的字符,称为“懒惰模式”[[49](ch26s06.html#ftn.id3120698)]
例如,使用贪婪模式`a.+b`匹配:
```
`aaabab`
```
使用懒惰模式`a.+?b`匹配:
```
`aaab`ab
```
* * *
> [[49](ch26s06.html#id3120698)] `.+` 匹配一个或多个任意字符,在贪婪模式中,它匹配尽可能多的字符;而懒惰模式中(`.+?`),则只匹配一个字符;`.{3,5}`在贪婪模式中尽可能匹配5个字符,在懒惰模式中(`.{3,5}?`)只匹配3个字符;`?` 和 `*` 这样可以匹配零次的限定符,在懒惰模式下不匹配任何字符(`.*?`、`.??`)
## 分支条件
`|` 表示“或”,使用它进行分支选择
例如`[a-z]+|\d+`匹配单词或数字:
```
expression 123
```
## 分组、捕获
### 分组
使用`(表达式)`对表达式进行分组,例如使用`(\d{3}\.){2}`匹配下面例子中的数字:
```
abc`123.456.`def
```
`\d{3}`表示三个数字,`(\d{3}\.)`表示三个数字加“`.`”为一组,`{2}`表示这一组内容重复两次
### 捕获
在对表达式进行分组的时候,会捕获文本到自动命名的组里,使用 `\1 \2 ……` 后向引用组
例如用`([a-z]*)\ (\d*)`匹配下列文本,`([a-z]*)`为`\1`组,`(\d*)`为`\2`组
```
kardinal 1234567
```
使用`\2\ \1`替换`([a-z]*)\ (\d*)`,可以改变两个字符串的顺序
```
1234567 kardinal
```
如果分组较多,计数可能会不太方便,可以给分组指定名称,例如:
```
(?<**name**>[a-z]*)\ (?<**num**>\d*)
\k<**num**>\ \k<**name**> (?#使用“`\k<name>`”后向引用)
```
使用`(?:表达式)`,则只是分组,而不捕获,下面例子中,`(\d*)`为`\1`组
```
(?:[a-z]*)\ (\d*)
```
## 零宽断言
目前为止,我们学到的正则表达式匹配,都是有“宽度”的,使用 `\w+。` 匹配下面文本,会将 `。` 一同匹配:
```
regular。
expression。
```
如果不想匹配符号,只匹配一个位置,就要用到“零宽断言”(匹配宽度为零,满足一定的 条件/断言),零宽断言使用 **(?=表达式)** 的语法,例如 `\w+(?=。)`,其中 `(?=。)` 表示 `。` 前面的位置(先行断言)
```
`regular`。
`expression`。
```
如果需要匹配后面的位置,如:
```
。`regular`
。`expression`
```
则要用到后发断言 `(?<=。)` ,使用 `(?<=。)\w+` 得到上面的匹配结果
使用 `(?<=<b>).*(?=</b>)` 匹配标签中的内容
```
<b>`粗体`</b>
```
### 负向零宽断言
负向零宽断言 **(?!表达式)** 也是匹配一个零宽度的位置,不过这个位置的“断言”取表达式的反值,例如 `(?!表达式)` 表示 `表达式` 前面的位置,如果 `表达式` 不成立 ,匹配这个位置;如果 `表达式` 成立,则不匹配:
```
`expression`
`expression`,
`expression`;
expression。
```
以上为使用 `.+n(?!。)` 的匹配结果。注意与 `.+n[^。]` 匹配的区别
```
expression
`expression,`
`expression;`
expression。
```
同样,负向零宽断言也有“先行”和“后发”两种,负向零宽后发断言为 **(?<!表达式)**
使用 `(?<![</])para(?!>)` 匹配下面文本
```
<para>`para`表示一个段落</para>
```
* `(?<![</])` 表示 `para` 左边不能为 `<` 或 `/` ;`(?!>)` 表示 `para` 右边不能为 `>`
- 开源世界旅行手册
- 授权
- 致谢
- 序言
- 更新纪录
- 导读
- 如何写作科技文档
- 部分 I. 气候
- 第 1 章 GUI? CLI?
- 第 2 章 UNIX 缩写风格
- 第 3 章 版本号的迷雾
- 第 4 章 Vim 还是 Emacs
- 第 5 章 DocBook 还是 TeX
- 第 6 章 完全用 Gnu/Linux 工作
- 第 7 章 病毒
- 第 8 章 磁盘 分区
- 第 9 章 文件系统
- 第 10 章 发行版介绍
- 第 11 章 编程语言
- 第 12 章 无根的根:无名师的 Unix 心传
- 部分 II. 地理
- 第 13 章 基础知识
- 第 14 章 命令系统
- 第 15 章 基本系统
- 第 16 章 软件管理
- 第 17 章 核心工具集
- 第 18 章 编译工具链
- 第 19 章 图形界面
- 第 20 章 国际化
- 第 21 章 内核
- 第 22 章 Grub
- 第 23 章 服务器
- 第 24 章 Vim 编辑器
- 第 25 章 Emacs 入门
- 第 26 章 正则表达式
- 第 27 章 docbook 指南
- 第 28 章 Git 版本控制系统
- 第 29 章 ConTeXt 入门指南
- 部分 III. 景观
- 第 30 章 终极 Shell -- ZSH
- 第 31 章 完美工作站 Archlinux
- 第 32 章 组织你的意念:Emacs org mode
- 第 33 章 Zsh+screen
- 第 34 章 gentoo stage3
- 第 35 章 硬件问题
- 第 36 章 网络设置
- 第 37 章 自制 LiveCD
- 第 38 章 awesome
- 第 39 章 openbox 工作环境
- 第 40 章 Emacs muse
- 第 41 章 写作工具链
- 第 42 章 使用 lftp
- 第 43 章 Firefox 使用技巧
- 第 44 章 FVWM
- 部分 IV. 地质
- 第 45 章 Unix
- 第 46 章 Gnu
- 第 47 章 软件业自由之神——Richard Stallman
- 第 48 章 Linux
- 第 49 章 GNOME与KDE的战争
- 第 50 章 Vim Emacs
- 第 51 章 年代纪
- 第 52 章 我的选择
- 第 53 章 补遗