[原创文章,转载请保留或注明出处:http://www.regexlab.com/zh/regref.htm]
正则表达式(regular expression)作为一种强大的字符串匹配工具,其核心在于定义特定模式,以实现两大核心功能:其一,检测字符串中是否存在符合规则的子串,并精准定位该子串的内容与位置;其二,依据匹配规则对字符串进行灵活的替换操作,极大提升文本处理的效率。
正则表达式的学习门槛远低于大众认知,其核心概念抽象性有限,易于理解。然而,多数学习者感到困惑的根源往往在于两方面:部分教学文档未能遵循由浅入深的逻辑顺序,概念讲解缺乏条理性,导致读者理解障碍;各引擎自带文档常侧重介绍其特有功能,而这类非通用特性并非初学者优先掌握的重点。本文将通过实例演示与交互式测试(文中每个举例均可点击进入测试页面),系统化梳理正则表达式的知识体系,助力读者快速掌握其精髓。
普通字符包括字母、数字、汉字、下划线,以及未在后续章节中被赋予特殊含义的标点符号。在匹配过程中,普通字符需与目标字符串中的字符完全一致方可实现匹配。例如,表达式“c”在匹配字符串“abcde”时,成功匹配到字符“c”,其位置为索引2至3(注:索引起始值因编程语言而异);而表达式“bcd”在匹配同一字符串时,则成功匹配子串“bcd”,位置为索引1至4。
部分难以直接书写的字符需通过转义符号“\”进行表示,这些字符均为开发者所熟知的控制字符。具体而言:
- `\r`与`\n`分别代表回车符与换行符;
- `\t`代表制表符;
- `\\`代表反斜杠“\”本身。
部分标点符号在正则表达式中具有特殊含义(如`^`、`$`),若需匹配其字面值,需在符号前添加“\”。例如:
- `\^`匹配“^”字符本身;
- `\$`匹配“$”字符本身;
- `\.`匹配小数点“.”本身。
此类转义字符的匹配逻辑与普通字符一致,均要求与目标字符完全对应。例如,表达式`\$d`在匹配字符串“abc$de”时,成功匹配子串“$d”,位置为索引3至5。
正则表达式通过特定元字符实现“匹配任意一个字符”的灵活功能,此类元字符虽能匹配多种字符之一,但每次仅匹配单个字符,类似于扑克牌中“万能牌”的使用逻辑。常用元字符包括:
- `\d`:匹配任意数字(0-9);
- `\w`:匹配任意字母、数字或下划线(A-Z、a-z、0-9、_);
- `\s`:匹配任意空白字符(如空格、制表符、换页符);
- `.`:匹配除换行符(`\n`)外的任意字符。
例如,表达式`\d\d`在匹配字符串“abc123”时,成功匹配子串“12”,位置为索引3至5;表达式`a.\d`在匹配“aaa100”时,匹配到“aa1”,位置为索引1至4。
方括号`[]`提供了自定义字符集匹配的功能:`[字符序列]`匹配序列中的任意一个字符,而`[^字符序列]`则匹配序列外的任意一个字符。需注意的是,此类匹配同样限定为单字符。例如:
- `[ab5@]`匹配“a”“b”“5”“@”中的任意一个;
- `[^abc]`匹配非“a”“b”“c”的任意字符;
- `[f-k]`匹配“f”至“k”范围内的任意小写字母;
- `[^A-F0-3]`匹配非“A”至“F”且非“0”至“3”的任意字符。
举例而言,表达式`[bcd][bcd]`在匹配“abc123”时,成功匹配“bc”,位置为索引1至3;表达式`[^abc]`在匹配同一字符串时,匹配到“1”,位置为索引3至4。
为避免重复书写表达式,正则表达式引入了修饰匹配次数的元符号,此类符号置于被修饰表达式之后,实现重复匹配功能。常用符号包括:
- `{n}`:表达式重复n次(如`\w{2}`等价于`\w\w`);
- `{m,n}`:表达式重复m至n次(如`ba{1,3}`匹配“ba”“baa”“baaa”);
- `{m,}`:表达式至少重复m次(如`\w\d{2,}`匹配“a12”“_456”等);
- `?`:匹配0次或1次(等价于`{0,1}`,如`a[cd]?`匹配“a”“ac”“ad”);
- `+`:至少匹配1次(等价于`{1,}`,如`a+b`匹配“ab”“aab”等);
- ``:匹配0次或任意次(等价于`{0,}`,如`\^b`匹配“b”“^^^b”等)。
例如,表达式`\d+\.?\d`在匹配“It costs $12.5”时,成功匹配“12.5”,位置为索引10至14;表达式`go{2,8}gle`在匹配“Ads by goooooogle”时,匹配到“goooooogle”,位置为索引7至17。
部分符号在表达式中具有抽象的定位或逻辑功能:
- `^`:匹配字符串开头位置(不匹配字符);
- `$`:匹配字符串结尾位置(不匹配字符);
- `\b`:匹配单词边界(单词与空格之间的位置,不匹配字符);
- `|`:实现“或”逻辑(匹配左侧或右侧表达式);
- `( )`:作用包括:① 作为整体被修饰次数修饰;② 提取子匹配结果。
举例说明:表达式`^aaa`仅匹配字符串开头的“aaa”(如“aaa xxx xxx”),在“xxx aaa xxx”中匹配失败;表达式`\bend\b`在“weekend,endfor,end”中仅匹配最后一个“end”(位置索引15至18);表达式`Tom|Jack`在“I'm Tom, he is Jack”中先后匹配“Tom”与“Jack”;表达式`¥(\d+\.?\d)`在匹配“$10.9,¥20.5”时,可提取子匹配结果“20.5”。
默认情况下,修饰匹配次数的符号(如`{m,n}`、``、`+`等)采用“贪婪模式”,即尽可能多地匹配字符。例如,表达式`(d)(\w+)`在匹配“dxxxdxxxd”时,`\w+`会匹配“xxxdxxxd”;而表达式`(d)(\w+)(d)`中,`\w+`为使整体匹配成功,会“让出”最后一个“d”,匹配“xxxdxxx”。
“非贪婪模式”(或称“勉强模式”)通过在修饰符号后添加`?`实现,此时表达式会尽可能少地匹配字符。例如,表达式`(d)(\w+?)`在匹配“dxxxdxxxd”时,`\w+?`仅匹配第一个“x”;表达式`(.?)`在匹配`aa bb`时,会分别匹配两个``标签内的内容,而非贪婪地匹配整个字符串。
正则表达式引擎会记录括号`()`内子表达式匹配的内容,并在后续匹配中通过`\数字`引用。例如:
- 表达式`('|")(.?)\1`在匹配` 'Hello', "World" `时,`\1`引用第一个括号匹配的`'`或`"`,确保引号配对;
- 表达式`(\w)\1{4,}`匹配“aa bbbb abcdefg ccccc 111121111 999999999”时,`\1`引用第一个`\w`匹配的字符,匹配“ccccc”与“999999999”(要求同一字符重复至少5次)。
预搜索(正向与反向)用于对“字符缝隙”附加条件,但不匹配字符本身:
- 正向预搜索`(?=xxxxx)`:要求右侧匹配`xxxxx`,如`Windows (?=NT|XP)`仅匹配“Windows NT”中的“Windows ”;
- 正向否定预搜索`(?!xxxxx)`:要求右侧不匹配`xxxxx`,如`do(?!\w)`匹配“done, do, dog”中的“do”(后跟非单词字符);
- 反向预搜索`(?<=xxxxx)`:要求左侧匹配`xxxxx`,如`(?<=\d{4})\d+(?=\d{4})`匹配“1234567890123456”中间8位数字;
- 反向否定预搜索`(?
- `\xXX`:表示0-255范围内的字符(如`\x20`表示空格);
- `\uXXXX`:通过Unicode编码表示任意字符(如`\u4E2D`表示“中”)。
- `\S`:非空白字符(`\s`的相反);
- `\D`:非数字字符(`\d`的相反);
- `\W`:非字母、数字、下划线字符(`\w`的相反);
- `\B`:非单词边界(`\b`的相反)。
需通过`\`转义的字符包括:`^`、`$`、`( )`、`[ ]`、`{ }`、`.`、`?`、`+`、``、`|`。
`(?:xxxxx)`表示括号内表达式不记录匹配结果,如表达式`(?:(\w)\1)+`匹配“a bbccdd efg”时,“(?:)”内的`\w`匹配结果不被记录,`\1`仍可引用外层括号内容。
- `Ignorecase`:忽略大小写;
- `Singleline`:`.`匹配包括换行符在内的所有字符;
- `Multiline`:`^`与`$`匹配每行开头与结尾;
- `Global`:替换所有匹配项(而非仅第一个)。
- 完整匹配:使用`^`与`$`确保匹配整个字符串(如`^\d+$`匹配纯数字字符串);
- 完整单词匹配:使用`\b`(如`\b(if|while|else)\b`匹配程序关键词);
- 避免空字符串匹配:如匹配“123”“123.”“123.5”“.5”时,避免`\d\.?\d`(可匹配空),改用`\d+\.?\d|\.\d+`;
- 防止死循环:避免“可匹配0次的子表达式+无限次修饰”(如`(\d)`可能导致引擎死循环);
- 合理选择贪婪/非贪婪模式:根据需求平衡匹配精度与效率;
- 优化“或”逻辑:确保`|`两侧表达式对同一字符的匹配范围互斥,避免结果因顺序变化而不同。