正则表达式

正则表达式(Regex)是一种强大的工具,用于在文本中进行模式匹配、搜索、替换和验证。掌握其规则需要理解其基本构成元素和语法。下面详细解析正则表达式的书写规则:

核心概念:

正则表达式由普通字符(如字母、数字)和特殊字符(称为元字符,具有特殊含义)组成。通过组合这些字符,你可以定义复杂的搜索模式。


一、基本匹配

  • 普通字符: 绝大多数字母(a-z, A-Z)、数字(0-9)和部分标点符号(如空格)直接匹配它们自身。
    • 示例:cat 匹配字符串中的 “cat”。
  • 字面匹配特殊字符: 元字符(如 ., *, ?, +, ^, $, (, ), [, ], {, }, |, \)本身具有特殊含义。要匹配它们自身,需要在前面加上反斜杠 \ 进行转义
    • 示例:\. 匹配一个实际的句点 “.”;\* 匹配一个实际的星号 “*”;\\ 匹配一个实际的反斜杠 “”。

二、元字符及其含义

1. 单个字符匹配

  • . (点号): 匹配任意单个字符(除了换行符 \n,除非使用 s 修饰符)。
    • 示例:.at 匹配 “cat”, “hat”, “bat”, “mat”, “@at”, " at" 等。
  • [...] (字符类): 匹配方括号内的任意一个字符。
    • [aeiou] 匹配任意一个元音字母。
    • [a-z] 匹配任意一个小写字母(范围)。
    • [A-Za-z0-9] 匹配任意一个字母或数字。
    • [^...] (脱字符在开头):否定字符类,匹配不在方括号内的任意一个字符。
      • [^aeiou] 匹配任意一个非元音字母(包括辅音、数字、符号等)。
      • [^0-9] 匹配任意一个非数字字符。
  • \d 匹配任意一个数字。等价于 [0-9]
  • \D 匹配任意一个非数字字符。等价于 [^0-9]
  • \w 匹配任意一个单词字符(通常包括字母、数字、下划线 _)。等价于 [a-zA-Z0-9_]
  • \W 匹配任意一个非单词字符。等价于 [^a-zA-Z0-9_]
  • \s 匹配任意一个空白字符(包括空格、制表符 \t、换行符 \n、回车符 \r、换页符 \f)。
  • \S 匹配任意一个非空白字符

2. 位置锚点

  • ^ (脱字符):
    • 在正则表达式开头:匹配字符串的开始位置
      • 示例:^Hello 匹配以 “Hello” 开头的字符串。
    • 在字符类 [...] 内部开头:表示否定(见上文)。
  • $ (美元符): 匹配字符串的结束位置(或字符串结束前的换行符 \n 之前的位置,如果使用 m 修饰符)。
    • 示例:world$ 匹配以 “world” 结尾的字符串。
  • \b 匹配一个单词边界(单词字符 \w 与非单词字符 \W 之间的位置,或者字符串的开始/结束位置)。
    • 示例:\bcat\b 匹配整个单词 “cat”,不会匹配 “catalog” 或 “concatenate” 中的 “cat”。
  • \B 匹配一个非单词边界

3. 量词(指定前面元素出现的次数)

  • * (星号): 匹配前面的元素零次或多次
    • 示例:ab*c 匹配 “ac”, “abc”, “abbc”, “abbbc”, …(0个或多个 ‘b’)。
  • + (加号): 匹配前面的元素一次或多次
    • 示例:ab+c 匹配 “abc”, “abbc”, “abbbc”, …(至少1个 ‘b’),不匹配 “ac”。
  • ? (问号):
    • 匹配前面的元素零次或一次(即可选)。
      • 示例:colou?r 匹配 “color” 或 “colour”(‘u’ 可有可无)。
    • 用在量词 *, +, ?, {n}, {n,}, {n,m} 之后:表示非贪婪匹配(也称为懒惰匹配)。默认的贪婪匹配会匹配尽可能长的字符串,非贪婪匹配会匹配尽可能短的字符串。
      • 示例:a.*?b 匹配 “a” 和 “b” 之间最短的字符串。对于 “axb yb”,贪婪匹配 a.*b 会匹配整个 “axb yb”,而非贪婪匹配 a.*?b 会先匹配 “axb”。
  • {n} 匹配前面的元素恰好 n 次
    • 示例:a{3} 匹配 “aaa”。
  • {n,} 匹配前面的元素至少 n 次
    • 示例:a{2,} 匹配 “aa”, “aaa”, “aaaa”, …(至少2个 ‘a’)。
  • {n,m} 匹配前面的元素至少 n 次,但不超过 m 次
    • 示例:a{2,4} 匹配 “aa”, “aaa”, “aaaa”(2到4个 ‘a’)。

4. 分组和捕获

  • (...) (圆括号):
    • 分组: 将多个元素组合成一个子表达式,以便应用量词或逻辑操作。
      • 示例:(ab)+ 匹配 “ab”, “abab”, “ababab”, …(一个或多个 “ab” 序列)。
    • 捕获: 匹配括号内子表达式的内容,并存储在捕获组中,后续可通过回溯引用\1, \2, …)或在处理结果中访问(如 match.group(1))。
      • 示例:(a)(b)\1\2 匹配 “abab”(\1 引用第一个捕获组 “(a)” 匹配的 ‘a’,\2 引用第二个捕获组 “(b)” 匹配的 ‘b’)。
  • (?:...) (非捕获分组): 只进行分组,不捕获内容。性能通常优于捕获分组,当你不需要引用匹配内容时使用。
    • 示例:(?:ab)+ 匹配 “ab”, “abab”, …,但不会创建捕获组。

5. 逻辑操作(选择)

  • | (竖线): 表示(alternation)。匹配左边或右边的子表达式。
    • 示例:cat|dog|bird 匹配 “cat”、“dog” 或 “bird”。
    • 示例:gr(a|e)y 匹配 “gray” 或 “grey”(等价于 gr[ae]y)。

6. 转义序列

  • \ + 特定字符: 除了用于转义元字符本身(如 \., \*),还用于表示特殊字符:
    • \n:换行符 (LF)
    • \r:回车符 (CR)
    • \t:水平制表符 (Tab)
    • \f:换页符
    • \v:垂直制表符(某些实现)
    • \xHH:匹配 ASCII 码为十六进制值 HH 的字符(如 \x41 匹配 ‘A’)。
    • \uHHHH:匹配 Unicode 码点为十六进制值 HHHH 的字符(如 \u00A9 匹配版权符号 ©)。
    • \cX:匹配控制字符(如 \cC 匹配 Ctrl-C)。

7. 预定义字符类(部分实现)

  • [:alpha:]:字母(等价于 [a-zA-Z]
  • [:digit:]:数字(等价于 [0-9]\d
  • [:alnum:]:字母或数字(等价于 [a-zA-Z0-9]
  • [:space:]:空白字符(等价于 \s
  • [:lower:]:小写字母
  • [:upper:]:大写字母
  • [:punct:]:标点符号
  • [:graph:]:可见字符(非空白、非控制字符)
  • [:print:]:可打印字符(包括空格)
  • [:cntrl:]:控制字符
  • [:xdigit:]:十六进制数字([0-9a-fA-F]
  • 注意: 这些通常需要在POSIX字符类中使用,形式为 [[:digit:]]。它们的具体定义可能因语言/环境略有差异。

8. 零宽断言(Lookaround Assertions)

这些结构用于检查一个位置是否满足某些条件,但不消耗任何字符(即它们匹配的是位置,而不是文本)。

  • (?=...) (肯定顺序环视 / 正向先行断言): 断言当前位置之后能匹配表达式 ...
    • 示例:Windows (?=95|98|NT|2000) 匹配 "Windows " 仅当其后紧跟 “95”, “98”, “NT” 或 “2000” 时(匹配结果只包含 "Windows ")。
  • (?!...) (否定顺序环视 / 负向先行断言): 断言当前位置之后不能匹配表达式 ...
    • 示例:Windows (?!95|98|NT|2000) 匹配 "Windows " 仅当其后紧跟 “95”, “98”, “NT” 或 “2000” 时。
  • (?<=...) (肯定逆序环视 / 正向后行断言): 断言当前位置之前能匹配表达式 ...
    • 示例:(?<=95|98|NT|2000) Windows 匹配 " Windows" 仅当其前是 “95”, “98”, “NT” 或 “2000” 时(匹配结果只包含 " Windows")。
  • (?<!...) (否定逆序环视 / 负向后行断言): 断言当前位置之前不能匹配表达式 ...
    • 示例:(?<!95|98|NT|2000) Windows 匹配 " Windows" 仅当其前不是 “95”, “98”, “NT” 或 “2000” 时。
  • 注意: 后行断言 (?<=...)(?<!...) 在很多实现中(尤其是JavaScript)对子表达式的长度或复杂性有限制(例如不能是任意长度或包含量词)。

三、修饰符(Flags / Options)

修饰符通常添加在正则表达式结束分隔符之后(如 /pattern/flags),用于改变匹配行为的全局规则。

  • i (ignore case): 忽略大小写。[A-Z] 也会匹配小写字母,反之亦然。
  • g (global): 全局匹配。查找所有匹配项,而不仅仅是第一个。
  • m (multiline): 多行模式。使 ^$ 分别匹配每一行的开头和结尾(由换行符 \n 或回车符 \r 界定),而不仅仅是整个字符串的开头和结尾。
  • s (single line / dotall): 点号 . 将匹配包括换行符 \n 在内的所有字符(默认不匹配换行符)。
  • u (unicode): 启用完整的 Unicode 支持。正确处理四字节的 UTF-16 编码字符(如表情符号),并允许在模式中使用 \u{...} 表示法。
  • y (sticky): 粘性匹配。要求匹配必须从目标字符串的当前索引位置(lastIndex 开始。主要用于高效的流式解析。
  • x (extended / verbose): 忽略模式中的空白和注释(# 到行尾),提高复杂正则的可读性(支持度较低,Python 的 re.VERBOSE 或 Perl/PHP 的 /x 修饰符支持)。

四、书写技巧和注意事项

  1. 明确目标: 清晰定义你要匹配什么模式。
  2. 从简单开始: 先写核心匹配部分,再逐步添加边界条件、量词、分组等。
  3. 测试!测试!测试! 使用在线正则表达式测试工具(如 regex101.com, regexr.com)或编程语言中的调试功能,用各种正面和负面测试用例验证你的正则表达式。
  4. 可读性:
    • 对于复杂正则,使用 x 修饰符(如果支持)或添加注释(如 (?# 这是一个注释))。
    • 合理使用空格(如果 x 修饰符可用)或换行(在支持多行模式的编辑器中)。
    • 使用非捕获分组 (?:...) 代替不必要的捕获分组 (...)
  5. 效率:
    • 避免嵌套量词(如 (a+)+)或可能导致灾难性回溯的模式。这会使匹配时间指数级增长。
    • 优先使用具体字符类 [abc] 而不是点号 .(如果可能)。
    • 使用锚点 ^, $, \b 缩小匹配范围。
    • 谨慎使用回溯引用,它们可能影响效率。
    • 考虑使用原子分组 (?>...) 或占有量词 *+, ++, ?+, {n,m}+(如果支持)来防止回溯。例如 a++ 等价于 (?>a+)
  6. 转义: 注意在字符串中写正则时,编程语言本身可能也需要转义反斜杠。例如,匹配一个反斜杠需要写成 "\\\\"(字符串中的 \\ 表示一个 \,正则引擎看到的就是 \)。
  7. 特定语言差异: 不同编程语言(Perl, Python, JavaScript, Java, .NET, PHP等)的正则引擎在特性支持(如后行断言、原子分组、修饰符名称)和具体行为上可能存在细微差异。查阅目标语言的文档。
  8. 安全: 避免使用不受信任用户提供的正则表达式,尤其是复杂的或可能导致回溯爆炸的表达式(ReDoS 攻击)。

五、综合示例

  • 匹配简单邮箱(非常基础,不完美): ^\w+@\w+\.\w+$
    • ^ 字符串开始
    • \w+ 1个或多个单词字符(用户名)
    • @ 字面 @ 符号
    • \w+ 1个或多个单词字符(域名)
    • \. 字面点号 (.)
    • \w+ 1个或多个单词字符(顶级域名,如 com, org)
    • $ 字符串结束
  • 匹配日期 (YYYY-MM-DD): ^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$
    • ^ 开始
    • \d{4} 4位数字(年)
    • - 连字符
    • (0[1-9]|1[0-2]) 分组:0[1-9] (01-09) 或 1[0-2] (10-12)(月)
    • - 连字符
    • (0[1-9]|[12][0-9]|3[01]) 分组:0[1-9] (01-09) 或 [12][0-9] (10-29) 或 3[01] (30-31)(日)
    • $ 结束
  • 匹配双引号内的文本: "([^"]*)""([^"\\]*(?:\\.[^"\\]*)*)" (支持转义引号,如 \")
  • 移除HTML标签(非常简化): </?[^>]+> (使用 g 标志进行全局替换为 '')
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值