正则表达式⚓︎
约 2746 个字 预计阅读时间 14 分钟
1. 什么是正则表达式 (Regular Expression)⚓︎
正则表达式(Regex)是用于描述字符排列和匹配规则的工具。换句话说,它是记录文本规则的代码。
- 类比: 你可能使用过 Windows/Dos 下的文件查找通配符(Wildcard),如
*和?。例如查找所有 Word 文档会搜*.doc。 - 区别: 正则表达式比通配符更精确。通配符中的
*代表任意字符串,而正则可以精确描述“以 0 开头,后跟 2-3 个数字,然后是连字号,最后是 7-8 位数字”这样的复杂规则。
2. 核心语法与元字符 (Metacharacters)⚓︎
元字符是正则表达式中具有特殊含义的字符,而不是字符本身。
2.1 常用元字符表⚓︎
掌握它们的关键在于:看它们是在“方括号外面”还是“方括号里面”。
方括号分组[]⚓︎
12 个特殊字符分为三组。
第一组:[]内外不一样(3个):^, -, ]⚓︎
这几个字符在 [] 内外含义完全不同,是初学者最容易晕的地方。
| 字符 | 在 [] 外面 (默认) | 在 [] 里面 (特殊语境) | 例子对比 |
|---|---|---|---|
^ |
行首锚点 匹配字符串的开头。 | 取反 (仅在开头时) 除了这些都匹配。 | ^A: 必须以 A 开头 [^A]: 除了 A 以外的字符 |
- |
普通字符 就是一个横线。 | 范围符号 (仅在中间时) 从 X 到 Y。 | A-Z: 匹配 "A-Z" 这个文本 [A-Z]: 匹配大写字母 |
] |
普通字符 (如果没有对应的 [ ) |
结束符 表示方括号的结束。 | []]: 匹配一个右方括号 (第一个 ] 必须转义或放首位) |
第二组:在 [] 内普通(8个):., *, +, ?, (), |, {}, $⚓︎
这些字符在方括号外面法力无边,但一旦进入方括号里面,它们就变成了普普通通的标点符号。这是很多人的盲区!
| 字符 | 在 [] 外面 (元字符) | 在 [] 里面 (变成普通字符) |
|---|---|---|
. |
通配符 匹配除换行外的任意字符。 | 小数点 只匹配 . 本身。 |
* |
量词 重复 0 次或多次。 | 星号 只匹配 * 本身。 |
+ |
量词 重复 1 次或多次。 | 加号 只匹配 + 本身。 |
? |
量词 重复 0 次或 1 次 (可选)。 | 问号 只匹配 ? 本身。 |
( ) |
分组 把几个字符绑在一起。 | 括号 只匹配 ( 或 ) 本身。 |
| |
分支 (或) 左边或右边。 | |
{ } |
量词范围 例如 {3,5} 重复3到5次。 |
花括号 只匹配 { 或 } 本身。 |
$ |
行尾锚点 匹配字符串的结尾。 | 美元符号 只匹配 $ 本身。 |
极简记忆法:
如果你在 [] 里面想匹配一个点号,不需要写成
[\.],直接写[.]就行了。
第三组:万能-反斜杠 \⚓︎
\ (Escape Character) 是正则表达式中最大的 BOSS,不受[]控制。它有两个相反的作用:
- 让特殊的变普通:
- 如果你想匹配一个真正的
.,必须写\.- 如果你想匹配一个真正的+,必须写\+- 如果你想匹配一个真正的\,必须写\\ - 让普通的变特殊:
-
d是字母 d,但\d是数字。 -w是字母 w,但\w是单词字符。 -s是字母 s,但\s是空白符。
综合实战测试⚓︎
下面这个看似乱码的正则表达式,分析:
\([\^.]\+\)
拆解分析:
[和]:这是一个字符集。\^:这里的^本来在[开头表示“取反”,但因为前面加了反斜杠\,它变成了普通的脱字符^。.:在[]里面,点号自动失去法力,变成了普通的小数点.。+:在[]外面,它是量词,表示“前面的东西重复 1 次或多次”。
这个表达式匹配的是:由 ^ 符号或者 . 符号组成的连续字符串。
- 匹配:
^ - 匹配:
... - 匹配:
^.^. - 不匹配:
abc(因为它没取反,只是匹配符号本身)
()分组⚓︎
主要是零宽断言的语法:?, =, <, !
2.2 字符转义⚓︎
如果你想查找元字符本身(如 . 或 *),需要使用 \ 进行转义。
- 查找点号
.:使用\.(Java 字符串写作"\\.") - 查找星号
*:使用\* - 查找反斜杠
\:使用\\ - 示例:
deerchao\.cn匹配 "deerchao.cn";C:\\Windows匹配 "C:Windows"。
2.3 重复 (Quantifiers)⚓︎
用于指定前面的字符或分组出现的次数。
| 语法 | 说明 | 示例 |
|---|---|---|
* |
重复零次或更多次 | Windows\d+ 匹配 Windows 后跟至少 1 个数字 |
+ |
重复一次或更多次 | ^\w+ 匹配开头的第一个单词 |
? |
重复零次或一次 | home-?made 匹配 "homemade" 或 "home-made" |
{n} |
重复 n 次 | \d{2} 匹配 2 个数字 |
{n,} |
重复 n 次或更多次 | \d{2,} 匹配至少 2 个数字 |
{n,m} |
重复 n 到 m 次 | \d{5,12} 匹配 5 到 12 位数字 |
2.4 字符类 (Character Classes)⚓︎
当没有预定义元字符时,使用方括号 [] 自定义字符集合。
[aeiou]: 匹配任意一个英文元音字母。[.?!]: 匹配标点符号(. 或 ? 或 !)。[0-9]: 等同于\d。[a-z0-9A-Z_]: 等同于\w(仅限英文环境)。
2.5 反义 (Negation)⚓︎
查找 不属于 某个字符类的字符。
| 语法 | 说明 | 示例 |
|---|---|---|
\W |
匹配任意不是字母、数字、下划线、汉字的字符 | [^\w] |
\S |
匹配任意不是空白符的字符 | \S+ 匹配不包含空白符的字符串 |
\D |
匹配任意非数字的字符 | [^0-9] |
\B |
匹配不是单词开头或结束的位置 | end\B 匹配 "ender" 中的 "end" |
[^x] |
匹配除了 x 以外的任意字符 | [^aeiou] 匹配非元音字母 |
[^aeiou] |
匹配除了 aeiou 这几个字母以外的任意字符 |
- 示例:
<a[^>]+>匹配用尖括号括起来的以 a 开头的字符串。
3. 进阶规则与逻辑⚓︎
3.1 分支条件 (Alternation)⚓︎
使用 | 把不同的规则分隔开,满足其中任意一种即可。注意顺序:从左到右匹配,满足一个即停止。
- 示例 1 (电话号码):
0\d{2}-\d{8}|0\d{3}-\d{7} - 匹配:3 位区号-8 位本地号 (010-12345678) 或 4 位区号-7 位本地号 (0376-2233445)。
- 示例 2 (美国邮编):
\d{5}-\d{4}|\d{5} - 匹配:5 位数字,或者 5 位-4 位。
- 陷阱: 如果写成
\d{5}|\d{5}-\d{4},对于 9 位邮编只会匹配前 5 位(因为左边先满足了)。优先满足左边,和if的优先满足左侧表达式一致
3.2 分组 (Grouping)⚓︎
使用小括号 () 将多个字符作为子表达式(分组),可以对其使用重复量词。
-
简单 IP 匹配:
(\d{1,3}\.){3}\d{1,3} -
含义:
(1-3位数字 加上点号)重复 3 次,最后再加上1-3位数字。 -
缺陷: 也会匹配 "256.300.888.999" 这种非法 IP。
-
精确 IP 匹配:
- 解析
2[0-4]\d: 200-249 - 解析
25[0-5]: 250-255 - 解析
[01]?\d\d?: 0-199 (包括 0-9, 00-99, 100-199)
3.3 后向引用 (Back References)⚓︎
捕获组的内容可以在表达式后续部分被再次引用。
- 原理:分组后,正则会在内存里为每个组依次匹配一个组号,可以调用组号 此外,组名还可以自己命名(不用默认组号)
- 默认组号: 从左向右,以左括号
(出现顺序编号,1, 2, 3... - 引用语法:
\1代表分组 1 匹配到的文本。 - 示例:
\b(\w+)\b\s+\1\b - 匹配重复单词,如 "go go", "kitty kitty"。
- 命名分组 (Named Groups):
- 语法:
(?<Word>\w+)或(?'Word'\w+) - 引用:
\k<Word> - 示例:
\b(?<Word>\w+)\b\s+\k<Word>\b
3.4 零宽断言 (Zero-width Assertions)⚓︎
用于查找在某些内容之前或之后的位置(类似 \b, ^, $,不消费字符,只匹配位置)。
- 零宽 (Zero-width): 它不占位。匹配完成后,正则的“指针”停留在原处,不会跳过这些字符。
- 断言 (Assertion): 它的作用是判断。就像在说:“我要求这个位置的前面(或后面)必须(或不能)是某些内容。”
| 名称 | 语法 | 说明 | 示例 |
|---|---|---|---|
| 正预测先行 | (?=exp) |
断言自身位置 后面 能匹配 exp | \b\w+(?=ing\b) 匹配 "singing" 中的 "sing" (查找 ing 前面的部分) |
| 正回顾后发 | (?<=exp) |
断言自身位置 前面 能匹配 exp | (?<=\bre)\w+\b 匹配 "reading" 中的 "ading" (查找 re 后面的部分) |
| 负预测先行 | (?!exp) |
断言自身位置 后面不能 匹配 exp | \d{3}(?!\d) 匹配后面不跟数字的 3 位数字 |
| 负回顾后发 | (?<!exp) |
断言自身位置 前面不能 匹配 exp | (?<![a-z])\d{7} 匹配前面不是小写字母的 7 位数字 |
- 复杂示例:
\b\w*q[^u]\w*\b匹配包含 q 但 q 后面不是 u 的单词。 - 注: 处理结尾 q 需更严谨,上面的表达式中,若 q 在结尾会出错。
- 更严谨写法:
\b((?!abc)\w)+\b匹配不包含连续字符串 abc 的单词。
3.5 贪婪与懒惰 (Greedy vs Lazy)⚓︎
- 贪婪 (默认): 匹配尽可能多的字符。
a.*b匹配aabab的整个字符串aabab。- 懒惰 (Reluctant): 匹配尽可能少的字符。在限定符后加
?。 a.*?b匹配aabab中的aab(第一段) 和ab(第二段)。- 常用懒惰限定符:
*?,+?,??,{n,m}?。
3.6 注释⚓︎
使用 (?#comment) 包含注释。
- 示例:
2[0-4]\d(?#200-249)|25[0-5](?#250-255)|[01]?\d\d?(?#0-199) - 建议开启“忽略模式里的空白符”选项以便排版。这样在编写表达式时能任意的添加空格,Tab,换行,而实际使用时这些都将被忽略。启用这个选项后,在#后面到这一行结束的所有文本都将被当成注释忽略掉。
例如,我们可以前面的一个表达式(?<=<(\w+)>).*(?=<\/\1>)写成这样:
4. 实战案例解析 (PPT 案例)⚓︎
案例 1: 复杂电话号码匹配⚓︎
需求: 匹配如 (010)88886666,022-22334455,02912345678。
正则:
分析:
\(?: 转义左括号,出现 0 或 1 次。0: 必须以 0 开头。\d{2}: 接着 2 个数字。[) -]?: 右括号、空格或连字号中的一个,出现 0 或 1 次。\d{8}: 最后 8 个数字。
- 注: 此表达式也会匹配
010)12345678这种不规范格式,更严谨需用分支条件。 - 改进版 (分支条件):
\(0\d{2}\)[- ]?\d{8}|0\d{2}[- ]?\d{8}
案例 2: 作业练习⚓︎
任务: 在一段文本中查找电话、Email、URL、IP,并转为带超链接的 HTML。
- 电话: 支持国内固话和手机。
- URL: 支持
http://,https://,ftp://及无前缀网址。 - Email: 标准邮箱格式。
- IP: IPv4 (可选 IPv6)。
- 输出格式:
<a href="...">显示文本</a> - 参考思路:
- 手机:
1[3-9]\d{9} - Email:
\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)* - URL:
(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]
5. 记忆方法 (Mnemonics)⚓︎
正则元字符通常是英文单词的首字母缩写,理解这些词源能帮你快速记忆。
5.1 常用缩写⚓︎
\w- Word (单词)- 记忆逻辑: 代表构成“单词”的字符。
- 解释: 在编程变量命名(Identifier)或英语单词中,最常用的字符就是字母、数字和下划线。虽然它不包含标点符号,但它包含了写一个“词”所需的基本元素。
\d- Digit (数字)- 记忆逻辑: D igit 的首字母。
- 解释: 纯粹的数字 (0-9)。
\s- Space (空白)- 记忆逻辑: S pace 的首字母。
- 解释: 看不见的“留白”,包括空格、制表符(Tab)、换行符。
\b- Boundary (边界)- 记忆逻辑: B oundary 的首字母。
- 解释: 它不匹配任何字符,只匹配“单词与非单词之间的 界限”。
5.2 大小写互补规则 (Not)⚓︎
正则中有一个通用的规律:大写字母通常代表小写字母的“反义”(Not)。
\w(Word) <->\W(Not Word / 非单词字符)\d(Digit) <->\D(Not Digit / 非数字)\s(Space) <->\S(Not Space / 非空白)
5.3 位置符号⚓︎
^(Caret / 开头)- 记忆方法:
- 键盘上
6的上面是个向上的箭头,代表“指向上方/顶端”,引申为 开头。 - 这是个鼻子的形状,鼻子长在脸的最 前面。
- 键盘上
$(Dollar / 结尾)- 记忆方法:
- 想象这是“结账”的时刻。钱($)总是最后才付的,所以代表结尾。
- 在美国的日期写法或某些脚本中,变量结束符有时与 $ 有关,象征末尾。
常规表达式中不代表字符本身的:
| 序号 | 名称 | 字符 | []外(常规情况下) | []内 | ()内 |
|---|---|---|---|---|---|
| 1 | 通配符 | . | |||
| 2 | 重复1 | * | |||
| 3 | 重复2 | + | |||
| 4 | 重复3 | ? | |||
| 5 | 括号1 | () | |||
| 6 | 括号2 | [] | |||
| 7 | 括号3 | {} | |||
| 8 | 分组 | | | |||
| 9 | 转义 | \ | |||
| 10 | 开始 | ^ | |||
| 11 | 结束 | $ | |||
| 12 | 连接符 | - | |||
| 13 | 感叹号 | ! | |||
| 14 | < | ||||
| 15 | = | ||||
| 16 | # |