正则表达式
目录
1 简介
正则表达式(regular expression, 缩写regex) 是文本处理方面功能最强大的工具之一.
正则表达式的两个基本用途是: 搜索 和 替换.
2 简单的匹配
有时, 简单的组合正则表达式的 元字符 和 纯文本 便足够解决很多问题.
基本的元字符
元字符 说明 .匹配任意单个字符 []定义字符集合, 匹配字符集合中的一个字符 [^]对字符集合求非 -定义一个区间(如[A-Z]) \对下一个字符转义 注: 一个完整的正则表达式,
\字符后面必然跟着一个字符.数量元字符
元字符 说明 *匹配前一个字符(子表达式)的零次或多次重复 *?*的懒惰型版本+匹配前一个字符(子表达式)的一次或多次重复 +?+的懒惰型版本?匹配前一个字符(子表达式)的零次或一次重复 {n}匹配前一个字符(子表达式)的 n次重复{m, n}匹配前一个字符(子表达式)至少 n次且至多 m 次重复{n, }匹配前一个字符(子表达式)至少 n次重复{n, }?{n, } 的懒惰型版本 注:
*和+在默认情况下是 贪婪匹配, 会匹配尽可能多的字符, 容易造成过度匹配. 所以在使用时, 考虑使用它的 懒惰版本.位置元字符
元字符 说明 ^匹配字符串的开头 \A匹配字符串的开头 $匹配字符串的结束 \Z匹配字符串的结束 \<匹配单词的开头 \>匹配单词的结束 \b匹配单词边界(开头和结束) \B\b的反义, 即单词内部特殊字符元字符
元字符 说明 [\b]退格字符 \c匹配一个控制字符 \d匹配任意数字字符 \D\d的反义\f换页符 \n换行符 \r回车符 \s匹配一个空白字符 \S\s的反义\t制表符 \v垂直制表符 \w匹配任意字母数字或下划线字符 \W\w的反义\x匹配一个十六进制数字 \0匹配一个八进制数字 注:
Windows使用\r\n作为文本行结束标签.Unix\Linux使用\n.POSIX字符类
字符类 说明 [:alnum:]任何一个字母或数字 [:alpha:]任何一个字母 [:blank:]空格或制表符 [:cntrl:]ASCII 控制字符(ASCII 0-31, 127) [:digit:]任何一个数字 [:graph:]和 [:print:]一样, 但不包括空格[:lower:]任何一个小写字母 [:print:]任何一个可打印字符 [:punct:]既不属于 [:alnum:]也不属于[:cntrl:]的任何一个字符[:space:]任何一个空白字符, 包括空格 [:upper:]任何一个大写字符 [:xdigit:]任何一个十六进制数字 注: 部分工具或编程语言不支持.
通过使用这些元字符和纯文本的组合已经可以解决很多问题, 但是很多时候, 仅靠这些元字符能够解决的问题是有限的, 这时, 便需要用到更高级的功能.
3 子表达式
子表达式可以对表达式进行 分组 和 归类.
子表达式必须用 ( 和 ) 括起来. ( 和 ) 是元字符, 要匹配它们需要用 \ 转义.
对于子表达式, 常用的一个例子是匹配 IP 地址.
假设一段文本是这样的:
The ip address is: [12.159.46.200]
我们要从中匹配出 IP 地址, 简单的做法可以像这样:
\d{1, 3}\.\d{1, 3}\.\d{1, 3}\.\d{1, 3}
\d{1, 3} 匹配连续的一到三个数字, \. 匹配字符 ., 这时, 虽然我们可以从示例文本中匹配出 IP 地址, 但是很繁琐.
这时, 如果使用子表达式就会很简单:
(\d{1, 3}\.){3}\d{1, 3}
(\d{1, 3}\.){3} 表示匹配连续的一到三个数字和一个点三次, 而 (\d{1, 3})\.)
便是子表达式.通过括号括起来的一段表达式, 方便重复的匹配.
另外, 在子表达式内部, 你可以用元字符 | 来将子表单式分为两个部分. 相当于C语言中的 ||,
只要两部分中的一个匹配成功就算匹配成功. 如: (19|20)\d 匹配 19x 或 20x 的数字.
同时, 子表达式允许嵌套, 嵌套的层次理论上没有限制, 但是嵌套太多不是一个好主意.
如: (([A-Z]{2})|([a-z]{2})) 可以匹配两个连续的大写字母或两个连续的小写字母.
4 回溯引用
回溯引用 允许正则表达式模式引用前面部分中定义的 子表达式 匹配的 结果.
回溯引用 的表示方式是 \ 后紧跟一个 数字, 如: \1 表示前面定义的 第一个 子表达式.
(\0 一般代表整个匹配模式).
例如, 对于一段网页文本:
<h1> title1 </h1> <h2> title2 </h2> <h2> title3 </h3> <h4> title4 </h4>
很明显, 其中第三个标题的格式是错误的, 如果要匹配出正确的标题, 我们可以使用 回溯引用 来办到:
<h([1-6])>.*?</h\1>
这个匹配模式使用的 回溯引用, \1 引用了子表达式 ([1-6]) 的匹配 结果, 当前面的子表达式匹配到一个数字的时候, 回溯引用代表的数字便是匹配到的这一个数字. 此时只有前后相同时才可以匹配成功.
4.1 回溯引用在替换操作中的使用
你可以在替换时引用匹配时使用的子表达式.
假设有一段文本:
=123=, =456=, =789=....
你想讲所有的 =xxx= 替换为 +xxx+, 这时, 便可以使用回溯引用.
匹配模式: =(\d{3})=
替换模式: +\1+
替换结果:
+123+, +456+, +789+....
注: JavaScript 请使用 $ 替换 \.
大小写转换
部分正则表达式允许使用以下元字符对字母进行大小写转换:
元字符 说明 \E 结束 \L 或 \U 的转换 \l 把下一个字符(子表达式)转换为小写 \L 把 \L 到 \E 之间的字符全部转换为小写 \u 把下一个字符(子表达式)转换为大写 \U 把 \U 到 \E 之间的字符全部转换为大写 例如, 将正确的标题转换为大写:
<h1> title1 </h1> <h2> title2 </h2> <h2> title3 </h3> <h4> title4 </h4>
匹配模式:
<h([1-6])>(.*?)</h\1>替换模式:
<h\1>\u\2</h\1>替换结果:
<h1> TITLE1 </h1> <h2> TITLE2 </h2> <h2> title3 </h3> <h4> TITLE4 </h4>
5 前后查找
使用正则表达式标记要匹配的文本的位置, 对该位置的前后内容进行查找.
很多时候, 我们想要匹配位于网页 <title> 标签类的文本. 此时我们便可以使用前后查找来完成工作.
前后查找分为: 向前查找 和 向后查找 (常见的正则表达式实现都支持前者, 但支持后者的就没那么多).
5.1 向前查找
向前查找指定了一个必须匹配但不在结果中返回的模式.
向前查找是由 ?= 开头的 子表达式. 需要匹配的文本跟在 = 后面.
例如匹配这段文本的URL前缀:
https://github.com https://gayhub.com ftp://@_@.com
匹配模式: .+(?=:)
匹配结果:
https https ftp
任何一个子表达式都可以转换为一个向前查找表达式, 只要给他加上一个 ?= 前缀即可.
5.2 向后查找
向后查找类似于向前查找, 操作符是: ?<=
例如匹配 <title> 标签之间的文本, 我们可以这样操作:
(?<=<title>).*?(?=</title>)
5.3 对前后查找取非
前面两种查找方式为: 正向前查找 和 正向后查找.
既然有正, 自然有负, 负向查找与给定模式不匹配的文本.
负向前查找: (?!)
负向后查找: (?<!)
如: 有数字 $123, 567, $23, 55, 模式 (?<=\$)\d+) 匹配 $123, $23.
而模式 (?<!\$)\d 匹配 567, 55.
6 嵌入条件
注: 并非所有正则表达式实现都支持条件处理.
正则表达式里的条件用 ? 来定义, 如: ?, ?=, ?<= 也算是条件查找了.
嵌入条件的使用情况:
- 根据有关回溯引用来进行条件处理
- 根据一个前后查找来进行条件处理
6.1 回溯引用条件
回溯引用条件只在前面的子表达式匹配取得成功的情况下才允许使用一个表达式.
语法: (?(backreference)true-regex), 其中 ? 表明这是一个条件, 括号里的 backreference 是一个回溯引用.
true-regex 是一个只在 backreference 存在时才被执行的表达式.
或: (?(backreference)true-regex|false-regex), 当 backreference 不存在时执行 false-regex 表达式.
如模式: (0X)?(?(1)[A-F0-9]{6}|\d{6})
这个模式的前面部分为: (0X)?, 当这个条件满足的时候, 后面的条件将会执行: [A-F0-9]{6}, 而不满足的时候会执行: \d{6}.
注: 回溯引用条件中的 (1) 不需要加 \, 即不需要 (\1) 这样.
6.2 前后查找条件
前后查找条件只在一个向前查找或一个向后查找取得成功的情况下才允许使用一个表达式.
语法: (?(look-regex)true-regex) (look-regex) 是一个向前或向后查找表达式, true-regex 是在向前或向后查找成功后执行的子表达式.
如模式: \d{5}(?(?=-)-\d{4}), (?=-) 定义了一个向前查找表达式, 当查找成功后, 会执行 -\d{4} 表达式.