简体中文
English 简体中文 繁體中文
Methodox 首页

正则表达式——全面指南

目录

  1. 简介
  2. 在线测试器
  3. 实用语法
  4. 全面参考
  5. 结语

1. 简介

文本——无论是日志文件、配置数据、用户输入还是文档——在编程中无处不在。从简单的脚本到完整的应用,开发者经常需要搜索、验证、提取或转换文本片段。虽然许多 IDE 和编辑器(如 Notepad++、VS Code 或 Sublime Text)提供了基本的查找和替换功能,但当你需要处理模式、条件逻辑或重复结构时,这些功能很快就会捉襟见肘。这正是正则表达式(regex)的用武之地。

从本质上讲,正则表达式是一种描述一组字符串的紧凑、声明式的方式。比如说,你不必手动逐行扫描文本来找出日志中的所有警告消息,而是可以编写一个模式,匹配时间戳、日志级别、文件名、电子邮件地址、URL——任何你能想到的内容。一旦理解了基础知识,正则表达式就可能成为你开发者工具箱中最强大的“武器”之一。

来看一段典型的日志:

2025-05-31 14:23:48 [Info] Starting application...
2025-05-31 14:23:50 [Info] Loading application data...
2025-05-31 14:23:55 [Warning] Missing font file: New Times Roman.
2025-05-31 14:24:22 [Info] The application has started, with 1 warning.
2025-05-31 14:25:01 [Error] Unhandled exception: NullReferenceException.

如果你想回答这样的问题:

  • “发出了哪些警告,分别在什么时候?”
  • “哪些行包含错误消息?”
  • “应用启动了多少次?”

你可以用自己喜欢的语言编写一个脚本,按空格拆分、检查各个标记并进行字符串比较。但这很快就会变得繁琐:时间戳格式的多变、可选字段的处理、空白字符的各种怪异情况,都可能让你的代码变得脆弱。

然而,借助一个精心编写的正则表达式,你可以匹配整行日志,并捕获时间戳、日志级别以及消息的其余部分——全部在一个模式中完成。更重要的是,正则表达式可以处理任何具有一致结构的基于文本的格式:CSV 文件、HTML/XML 标签、自定义配置格式,等等。

读完本文后,你将:

  1. 理解正则表达式背后的基本动机。
  2. 了解适用于日常任务的实用语法元素和示例。
  3. 拥有一份汇总最常用正则表达式结构的便捷参考。

无论你只是在编辑器中做过一些“查找和替换”,还是编写过简单的字符串处理代码,本指南都将帮你跨越这道鸿沟,并向你展示如何借助正则表达式更上一层楼。

2. 在线测试器

正则表达式测试器(.NET / C# 语法*)

选项 * 全局(g)始终开启,因此会显示所有匹配项。

替换(可选)

3. 实用语法

本节将介绍正则表达式模式的核心构建块。每个小节先介绍一个概念,然后给出真实场景中的示例——有些基于日志处理,有些则涉及验证电子邮件地址或从文本中提取数字等日常任务。

3.1 字面字符与简单匹配

在最简单的层面上,正则表达式可以只由字面字符组成。例如:

  • 模式 Error 会匹配文本中出现的任何子字符串“Error”。
  • 模式 2025-05-31 会匹配这个确切的日期字符串。

然而,真实世界中的文本很少能完美对齐。例如,日志消息中包含不断变化的时间戳和不同的日志级别。要以通用的方式匹配它们,你需要用到元字符。

要点: 正则表达式中的大多数字符都按字面匹配其自身,特殊的元字符(如 .、*、?、\ 等)除外。要匹配字面意义上的句点、方括号或其他元字符,你必须用反斜杠对其进行“转义”(用 \. 匹配字面的点,用 \[ 匹配字面的左方括号,依此类推)。

3.2 字符类([])与范围

字符类是用方括号括起来的一组字符,它恰好匹配该组中的一个字符。例如:

  • [AEIOU] 匹配任意一个大写元音字母。
  • [0-9] 匹配 0 到 9 之间的任意一个数字。
  • [A-Za-z0-9_] 匹配一个典型的单词字符(字母、数字或下划线)。

示例 1:匹配四位数年份

假设你想匹配 1900 到 2099 之间的任意年份:

^(19|20)[0-9]{2}$
  • ^ 和 $ 是锚点(我们稍后会介绍)。
  • (19|20) 匹配“19”或“20”。
  • [0-9]{2} 恰好匹配两个数字。
  • 合在一起,它匹配“1900”到“2099”。

示例 2:提取日志级别

给定这样一行日志:

2025-05-31 14:23:55 [Warning] Missing font file: New Times Roman.

你可以用以下模式匹配日志级别(Info、Warning、Error 等):

\[(Info|Warning|Error|Debug)\]
  • \[ 和 \] 匹配字面的方括号。
  • (Info|Warning|Error|Debug) 匹配这四个单词之一。

如果你预计还会有其他级别(例如 Trace 或 Fatal),可以把它们加到括号内:

\[(?:Info|Warning|Error|Debug|Trace|Fatal)\]

这里我们使用了 (?: … ) 来创建一个非捕获组(我们将在第 5 节中详细讨论分组)。

3.3 量词:重复控制

量词指定前面的元素应该匹配多少次。最常见的量词有:

  • *——匹配零次或多次(贪婪)。
  • +——匹配一次或多次(贪婪)。
  • ?——匹配零次或一次(使前面的标记成为可选)。
  • {n}——恰好匹配 n 次。
  • {n,m}——匹配 n 到 m 次(包含两端)。
  • {n,}——匹配 n 次或更多次。

示例 3:匹配时间戳

像 2025-05-31 14:23:48 这样的时间戳可以用以下模式匹配:

\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}
  • \d 是 [0-9] 的简写。
  • \d{4} 恰好匹配四个数字(年份)。
  • - 匹配字面的连字符。
  • \d{2} 匹配两个数字(月和日)。
  • \s+ 匹配一个或多个空白字符(空格、制表符)。
  • \d{2}:\d{2}:\d{2} 匹配 HH:MM:SS。

如果你确定日期和时间之间恰好只有一个空格,可以用 \s 代替 \s+。

示例 4:可选的文件扩展名

假设你有 report.txt 或 report 这样的文件名。要同时匹配两者,可以使用:

^report(?:\.txt)?$
  • ^ 和 $ 确保我们匹配整个字符串。

  • (?:\.txt)? 的意思是“可选地匹配‘.txt’”。

    • \. 匹配字面的点。
    • txt 匹配“txt”。
    • 组后面的 ? 使其成为可选。

3.4 锚点:定位匹配

锚点指定的是文本中的位置,而不是实际的字符:

  • ^——行/字符串的开头。
  • $——行/字符串的结尾。
  • \b——单词边界(位于 \w 和 \W 之间)。
  • \B——非单词边界。

示例 5:以“Error”开头的行

如果你想找出所有以单词“Error”(作为独立单词)开头的行,可以这样写:

^Error\b.*$
  • ^Error 确保该行以“Error”开头。
  • \b 断言此处为单词边界,这样“Errors”或“ErrorCode”就不会被匹配。
  • .* 匹配该行的其余部分(任意字符,零次或多次)。
  • $ 锚定行尾。

3.5 分组与捕获

圆括号 ( 和 ) 有两个主要用途:

  1. 分组:将多个标记视为一个整体(通常与量词或选择结构一起使用)。
  2. 捕获:存储该组匹配到的文本,以便之后取用(在代码中或替换字符串中)。

示例 6:捕获时间戳和级别

给定:

2025-05-31 14:23:55 [Warning] Missing font...

你可以这样写:

^(\d{4}-\d{2}-\d{2})\s+(\d{2}:\d{2}:\d{2})\s+\[(Info|Warning|Error|Debug)\]\s+(.*)$
  • (\d{4}-\d{2}-\d{2}) 捕获日期(Group 1)。

  • (\d{2}:\d{2}:\d{2}) 捕获时间(Group 2)。

  • (Info|Warning|Error|Debug) 捕获日志级别(Group 3)。

  • (.*) 捕获消息的其余部分(Group 4)。

  • 在大多数语言中,匹配之后你可以提取 Group 1、Group 2 等:

    • 例如在 C# 中:match.Groups[1].Value 给出日期。

如果你想分组但不捕获(用于选择结构或量词),请使用 (?: … )。例如:

(?:Info|Warning|Error|Debug)

不会创建带编号的反向引用。

3.6 常用元字符与转义序列

一些最常用的简写和元字符包括:

  • .——匹配除换行符以外的任意字符(在大多数风格中如此;有些风格允许启用“dotall”模式来匹配换行符)。
  • \d——数字(等价于 [0-9])。
  • \D——非数字(等价于 [^0-9])。
  • \w——单词字符(字母、数字、下划线;[A-Za-z0-9_])。
  • \W——非单词字符(任何不属于 \w 的字符)。
  • \s——空白字符(空格、制表符、换行符等)。
  • \S——非空白字符。
  • \t、\n、\r——制表符、换行符、回车符。

提示: 在代码中编写正则表达式时,你通常需要对反斜杠进行转义。例如,在 C# 中,你可以使用逐字字符串字面量(@"\d{4}-\d{2}-\d{2}")来避免双重转义。

3.7 实用示例:提取电子邮件地址

假设你有一份文档或配置文件,需要从中提取所有电子邮件地址。一个简单(但并不完全符合 RFC)的模式是:

\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b
  • \b——确保从单词边界开始。
  • [A-Za-z0-9._%+-]+——一个或多个字母、数字、点、下划线、百分号、加号或连字符(@ 之前的本地部分)。
  • @——字面的“@”符号。
  • [A-Za-z0-9.-]+——一个或多个字母、数字、点或连字符(域名)。
  • \.——字面的点。
  • [A-Za-z]{2,}——顶级域名至少包含两个字母(例如“com”、“org”、“io”)。
  • \b——在单词边界处结束,以避免部分匹配(这样就不会匹配到末尾的标点符号)。

测试一下

给定:

Please contact alice@example.com or bob.smith@my-domain.io for more info. Alternatively, reach out to admin@localhost if testing locally.
  • alice@example.com 匹配。
  • bob.smith@my-domain.io 匹配。
  • admin@localhost 不匹配,因为 localhost 没有“. + 两个或更多字母”形式的顶级域名。

3.8 实用示例:验证北美电话号码

北美编号计划(NANP)的电话号码通常是这样的:

  • 123-456-7890
  • (123) 456-7890
  • 123.456.7890
  • +1 (123) 456-7890

一个实用的正则表达式可以是:

^(\+1\s?)?           # Optional country code +1 and a space
(?:\(\d{3}\)|\d{3})  # Either (123) or 123
[ .-]?               # Separator: space, dot, or hyphen (optional)
\d{3}                # Next three digits
[ .-]?               # Separator again (optional)
\d{4}$               # Last four digits

压缩之后(去掉空白和注释):

^(\+1\s?)?(?:\(\d{3}\)|\d{3})[ .-]?\d{3}[ .-]?\d{4}$
  • ^ 和 $ 将模式锚定到整个字符串。
  • (\+1\s?)?——可选地匹配“+1”,其后可以跟一个可选的空格。
  • (?:\(\d{3}\)|\d{3})——括号内的三个数字,或者不带括号的三个数字。
  • [ .-]?——可选地匹配一个空格、点或连字符。
  • \d{3}——三个数字。
  • [ .-]?——再次匹配分隔符。
  • \d{4}——四个数字。

这个模式可以匹配大多数常见的北美号码格式。通过调整分组和量词,你可以使其适用于其他地区,或采用更严格/更宽松的规则。

3.9 实用示例:解析日志文件

回到我们的日志示例,假设你只想提取警告消息及其时间戳。你可以使用这样的正则表达式:

^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s+\[Warning\]\s+(.*)$
  • Group 1 捕获时间戳(2025-05-31 14:23:55)。
  • Group 2 捕获消息本身(Missing font file: New Times Roman.)。

在大多数编程语言中:

  • C#(使用 System.Text.RegularExpressions):

    var pattern = @"^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s+\[Warning\]\s+(.*)$";
    var matches = Regex.Matches(logContents, pattern, RegexOptions.Multiline);
    foreach (Match m in matches) {
        string timestamp = m.Groups[1].Value;
        string message   = m.Groups[2].Value;
        Console.WriteLine($"[{timestamp}] Warning: {message}");
    }
    
  • Python(使用 re 模块):

    import re
    
    pattern = r'^(\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2})\s+\[Warning\]\s+(.*)$'
    for line in log_contents.splitlines():
        m = re.match(pattern, line)
        if m:
            timestamp = m.group(1)
            message   = m.group(2)
            print(f"[{timestamp}] Warning: {message}")
    

即使你只在编辑器中单击过“Replace”(替换),这些示例也应该能让你清楚地感受到一个正则表达式有多么强大。你定义了“匹配直到 [Warning] 为止的所有内容,然后捕获其后的任何内容”,就能准确得到你想要的数据。

3.10 测试与调试正则表达式

刚接触正则表达式时,很容易忽略一些小错误——漏掉转义、放错量词的位置、忘记锚点。下面是一些建议:

  1. 使用在线测试器 regex101.com(PCRE、JavaScript、Python)、RegexStorm(适用于 .NET)或 regexr.com 等网站可以让你输入模式、粘贴测试文本,并实时查看匹配结果和高亮显示。

  2. 从简单开始,逐步扩展 如果你需要一个复杂的模式,先从一小部分开始。例如,先只用 \d{4}-\d{2}-\d{2} 测试日期,然后加上时间,再加上日志级别,依此类推。

  3. 为模式添加注释(在支持的情况下) 一些语言/风格支持“扩展”或“自由间距”模式,在这种模式下,你可以在正则表达式中添加空白和 # comments:

    (?x)            # Enable free-spacing mode
    ^               # Start of line
    (\d{4}-\d{2}-\d{2})   # Group 1: Date
    \s+             # One or more spaces
    (\d{2}:\d{2}:\d{2})   # Group 2: Time
    \s+             # One or more spaces
    \[Warning\]     # Literal [Warning]
    \s+             # One or more spaces
    (.*)            # Group 3: Message text
    $
    

    这让维护和日后的修改更加容易,对于复杂的正则表达式尤其如此。

  4. 尽早转义 如果你需要匹配特殊字符(.、*、?、+、[、]、(、)、|、^、$、\),除非你明确需要它们的特殊行为,否则一律用反斜杠对其进行转义。

4. 全面参考

下面汇总了最常用的正则表达式结构。可以把它当作一份快速参考的“速查表”。

注意: 不同的正则表达式风格(PCRE、Python、JavaScript、.NET、Java 等)在一些细节上有所不同,但下面的大部分条目都是通用的。存在特定于风格的说明时,会加以注明。

4.1 基本元字符

元字符 含义
. 匹配除换行符以外的任意字符(除非启用了“dotall”/“singleline”模式)。
^ 匹配行/字符串的开头。
$ 匹配行/字符串的结尾。
\b 单词边界(\w 与 \W 之间的过渡处)。
\B 非单词边界(与 \b 相反)。
\d 数字,等价于 [0-9]。
\D 非数字,等价于 [^0-9]。
\w 单词字符,等价于 [A-Za-z0-9_]。
\W 非单词字符,等价于 [^A-Za-z0-9_]。
\s 空白字符(空格、制表符、换行符、垂直制表符、换页符等)。
\S 非空白字符(任何不被 \s 匹配的字符)。
\t 制表符。
\n 换行符(LF)。
\r 回车符(CR)。
\f 换页符。
\v(特定于风格) 垂直制表符(部分风格支持)。
\\ 字面的反斜杠。
\. 字面的点。
\* 字面的星号。
\+ 字面的加号。
\? 字面的问号。
\{ \} 字面的花括号。
\[ \] 字面的方括号。
\( \) 字面的圆括号。
\| 字面的竖线/选择运算符。
\^ 字面的脱字符(字符串开头锚点)。
\$ 字面的美元符号(字符串结尾锚点)。

4.2 量词

量词 含义
* 匹配前面的标记零次或多次(贪婪)。
+ 匹配前面的标记一次或多次(贪婪)。
? 匹配前面的标记零次或一次(使其成为可选;也用于惰性量词)。
{n} 匹配前面的标记恰好 n 次。
{n,} 匹配前面的标记n 次或更多次(无上限)。
{n,m} 匹配前面的标记至少 n 次但不超过 m 次。
*?、+?、??、{n,m}? 上述量词的“惰性”或“非贪婪”版本(匹配尽可能少的字符)。

贪婪与惰性:

  • 贪婪量词(*、+、{n,m})会在仍能让模式其余部分匹配成功的前提下,尝试匹配尽可能多的文本。
  • 惰性量词(*?、+?、{n,m}?)会匹配尽可能少的文本,只要整体匹配能够成功即可。

示例:贪婪与惰性

  • 模式 a.*b 作用于文本 axxxbxxb 时,会匹配 axxxbxxb(贪婪:从第一个 a 到最后一个 b)。
  • 模式 a.*?b 会匹配 axxxb(惰性:从 a 到第一个 b)。

4.3 字符类与字符集

语法 匹配
[abc] 字符 a、b 或 c 中的任意一个。
[^abc] 除 a、b 或 c 之外的任意字符。
[a-z] 从 a 到 z 的任意小写字母。
[A-Z] 从 A 到 Z 的任意大写字母。
[0-9] 从 0 到 9 的任意数字。
[A-Za-z0-9_] 任意“单词”字符(通常等价于 \w)。
[[:alnum:]](POSIX) 字母和数字(POSIX 风格)。可在部分风格中使用(例如 GNU、支持 [[:alnum:]] 的 PCRE)。
[[:space:]]、[[:digit:]] 等 POSIX 字符类(各风格的支持情况不一)。

示例:匹配十六进制数字

^[0-9A-Fa-f]+$
  • 匹配任何完全由十六进制数字组成的非空字符串。

4.4 选择(|)与分组

  • 分组:( … )

    • 捕获组:保存匹配到的文本,以供反向引用或提取。
    • 非捕获组:(?: … ) 不保存匹配内容;适用于只需分组而无需捕获的场合。
  • 选择:A|B 匹配模式 A 或模式 B。

示例:匹配多种文件扩展名

^.*\.(?:jpg|jpeg|png|gif|bmp)$
  • .*\. 匹配直到点(含点)为止的任意文件名部分。
  • (?:jpg|jpeg|png|gif|bmp) 匹配这五种扩展名之一。
  • $ 确保扩展名位于末尾。

4.5 锚点与边界

锚点 含义
^ 字符串开头(在多行模式下为行首)。
$ 字符串结尾(在多行模式下为行尾)。
\A 字符串开头(不受多行模式影响)。
\z 或 \Z 字符串结尾(\z 是绝对结尾;\Z 允许末尾有一个可选的换行符)。
\b 单词边界(位于 \w 和 \W 之间)。
\B 非单词边界(与 \b 相反)。

多行模式(m 标志)

  • 如果启用多行模式(通常通过一个标志,例如 .NET 中的 RegexOptions.Multiline 或 Python 中的 re.MULTILINE),^ 和 $ 将匹配每一行的开头/结尾,而不是整个字符串的开头/结尾。

4.6 环视(零宽断言)

环视让你可以断言某个匹配之前或之后是(或不是)某些内容,而不会把这些“内容”包含在匹配结果中。

结构 含义
(?= … ) 正向先行断言:其后的内容必须匹配 …。
(?! … ) 负向先行断言:其后的内容必须不匹配 …。
(?<= … ) 正向后行断言:其前的内容必须匹配 …。
(?<! … ) 负向后行断言:其前的内容必须不匹配 …。

示例:匹配不含数字的密码

要匹配一个长度为 6–12 个字符且不包含任何数字的密码:

^(?=.{6,12}$)(?!.*\d).+$
  • (?=.{6,12}$)——确保长度在 6 到 12 之间。
  • (?!.*\d)——确保任何位置都没有数字(.*\d 的意思是“某处有一个数字”;(?! … ) 表示“字符串的其余部分不得匹配 .*\d”)。
  • .+$——最后,匹配一个或多个(任意)字符,直到结尾。

4.7 反向引用

捕获一个组之后,你可以在模式的后面部分引用它(以强制要求文本的两个部分完全相同),也可以在替换字符串中引用它。

  • \1、\2……引用 Group 1、Group 2 等匹配到的文本(在大多数风格中如此)。
  • 在替换字符串中(例如在 .NET 或 JavaScript 中),你可能会使用 $1、$2 等。

示例:匹配 HTML/XML 的开始标签和结束标签

要确保开始标签 <tag> 由同名的 </tag> 闭合:

^<([A-Za-z][A-Za-z0-9]*)\b[^>]*>(.*?)</\1>$
  • <([A-Za-z][A-Za-z0-9]*)\b[^>]*>

    • 在 Group 1 中捕获标签名(例如 div、span、p)。
    • \b 确保标签名之后是一个单词边界。
    • [^>]* 匹配开始标签中的任何属性。
  • (.*?)

    • 惰性地捕获元素内的任何内容(Group 2)。
  • </\1>

    • 匹配与 Group 1 中捕获的标签名完全相同的结束标签(例如 </div>)。

这个简单的模式可以匹配像 <p class="intro">Hello</p> 这样的完整元素,但它无法处理同名标签的嵌套元素。对于嵌套结构,你需要更高级的解析方法(通常超出了基本正则表达式所能稳健处理的范围)。

4.8 常用简写与转义序列

序列 含义
\d 数字(与 [0-9] 相同)。
\D 非数字(与 [^0-9] 相同)。
\w 单词字符 [A-Za-z0-9_]。
\W 非单词字符 [^A-Za-z0-9_]。
\s 空白字符 [ \t\r\n\f\v]。
\S 非空白字符。
\t、\n、\r、\f 制表符、换行符、回车符、换页符。
\0 空字符(U+0000)。
\xhh 十六进制代码为 hh(两位十六进制数字)的字符。
\uhhhh 代码为 hhhh(四位十六进制数字)的 Unicode 字符。(特定于风格:.NET、Java、带 u 标志的 JavaScript 等。)
\cX 控制字符(例如 \cA 为 U+0001)。
\Q … \E 在某些风格(如 .NET)中,\Q 开始一个引用区段(直到 \E 为止的所有内容都按字面处理)。

4.9 占有量词(量词后加 +)与原子组(特定于风格)

一些正则表达式风格(如 Java、PCRE)支持占有量词(*+、++、?+、{n,m}+),它们从不回溯:

  • 示例:a*+b

    • a*+ 匹配尽可能多的 a,并且不允许通过回溯让 b 得以匹配。

    • 如果输入是“aaaaabc”:

      • a*+ 匹配“aaaaa”。
      • 然后引擎尝试匹配 b。由于下一个字符是 b,整体匹配结果为“aaaaab”。

原子组 (?> … ) 有类似的效果:一旦 (?> … ) 内的子模式匹配成功,引擎就不会再回溯到其中。

注意: 并非所有风格都支持占有量词或原子组。如果你需要这些高级功能,请查阅你所用语言/运行时的文档。

4.10 标志(修饰符)与模式

大多数正则表达式引擎允许你通过标志启用或禁用某些行为。常见的标志包括:

标志 含义 典型语法
i 不区分大小写的匹配(.NET 中为 RegexOptions.IgnoreCase)。 内联:(?i)pattern。
m 多行模式:^ 和 $ 匹配各行的开头/结尾,而不仅是字符串的开头/结尾。 在 .NET 中:RegexOptions.Multiline 或 (?m)pattern。
s 单行模式或 dot-all 模式:. 也匹配换行符。 在 .NET 中:RegexOptions.Singleline 或 (?s)pattern。
x 自由间距/注释模式:忽略空白,并允许使用 # comments。 在 .NET 中:RegexOptions.IgnorePatternWhitespace 或 (?x)pattern。
U 非贪婪模式(PCRE):互换贪婪与惰性行为(很少使用)。 在 PCRE 中:(?U)pattern。
u Unicode 模式(特定于风格)。 在 JavaScript 中使用 /u 标志,或在 .NET 中使用 RegexOptions.CultureInvariant。

示例:内联标志

(?im)^[a-z]+\d+\s+$
  • (?i) 表示“不区分大小写”。
  • (?m) 表示“多行”。
  • 如果没有标志,^[a-z]+ 只会匹配小写字母,并且只在整个字符串的开头匹配;有了 i 和 m,它会在每一行上匹配小写或大写字母。

4.11 替换模式

当你使用正则表达式执行替换时,通常会在替换字符串中使用反向引用:

  • 在许多语言中(例如 JavaScript、Python),使用 $1、$2……
  • 在 .NET 中,通常同样使用 $1、$2。
  • 在一些较旧的工具或风格中,可能会使用 \1、\2。

示例:把“Last, First”调换为“First Last”

原始文本:

Smith, John
Doe, Jane

正则表达式查找模式:

^([A-Za-z]+),\s+([A-Za-z]+)$

替换模式:

$2 $1

替换之后:

John Smith
Jane Doe
  • ([A-Za-z]+) 在 Group 1 中捕获姓氏。
  • ([A-Za-z]+) 在 Group 2 中捕获名字。
  • $2 $1 在输出中交换两者的位置。

5. 结语

到现在,你应该已经扎实地掌握了:

  • 正则表达式为什么对文本处理极为宝贵。
  • 如何编写基本模式:字面字符、字符类、量词、锚点。
  • 如何使用分组、环视和反向引用进行更高级的匹配。
  • 哪些标记和结构构成了大多数实用正则表达式的“核心”。
  • 需要温习时去哪里查找(上面的参考部分,或 regexstorm.net/reference 这样的网站)。

只需几十分钟的练习——针对示例文本编写正则表达式——你就能迅速建立信心。请记住:

  1. 尽早测试、经常测试:在在线测试器或 IDE 的“Find in Files”(在文件中查找)中测试——实时看到匹配结果,远比手动肉眼检查代码更有启发。
  2. 可读性很重要。 如果模式变得难以阅读,请使用自由间距模式((?x))并添加注释。
  3. 对于常见任务,保持简单。你很少需要最晦涩的语法;匹配日志级别、日期、电子邮件地址或基本的 CSV 字段,通常用简单直接的字符类和量词就能完成。

掌握基础之后,你可以探索更高级的主题,例如:

  • 原子分组和占有量词(用于对性能要求很高的模式)。
  • 递归模式(在 PCRE 或一些高级风格中),用于匹配嵌套结构。
  • 条件子模式(在 PCRE 或 .NET 中),在决定模式的下一部分之前,先测试某个组是否已经匹配。

不过,这些主题就留待以后再谈。现在,请借助上面的示例和参考表,在你的下一个文本处理挑战中尝试运用正则表达式。用不了多久,你就会发现自己开始用模式来思考——而且再也回不去了。

恭喜你踏上了打造模式的旅程!把这份指南放在手边,收藏你最喜欢的正则表达式测试器,并记住:一个写得好的正则表达式,往往能把数小时的手动解析变成一行优雅的代码。

参考资料