返回博客

发布于 · 阅读约 1 分钟

给非开发者的正则表达式实用指南

一份对写作者、分析师、市场人员和所有「查找替换」重度用户友好、几乎不绕术语的 regex 教程。学会真正重要的语法,并直接拿走十条即用即得的配方。

非开发者为什么也该懂一点 regex

如果你曾在 Word、Google Docs、Notion、电子表格或你最爱的文本编辑器里打开过「查找和替换」,并暗自想「肯定有更快的办法」,你就是本指南的目标读者。Regex(regular expression,正则表达式)是一种小巧的模式语言,能把「查找替换」从钝器升级成手术刀。你不再搜索固定的某个词,而是描述你要找的东西的「形状」:「任意电话号码」「以日期开头的每一行」「这份混乱导出里的所有邮箱」。

Regex 之所以让人望而生畏,多半是因为面向程序员写的教程在第一段就甩出二十个符号。事实是,靠大约十个字符和几种模式,你就能高产到出奇。本文先讲清楚这个核心,再给你十条可直接粘进几乎任何现代应用「查找替换」框的配方。

顺手提一句 regex 在哪儿可用:Microsoft Word(勾选「使用通配符」或新版 regex 搜索)、Google Docs(「使用正则表达式匹配」)、Notion(有限)、VS Code、Sublime Text、BBEdit、Obsidian、Google Sheets 的 REGEXMATCH 与 REGEXREPLACE、Excel 的 Power Query 以及新版 REGEX 函数,几乎所有代码编辑器和在线测试工具也都支持。不同应用方言略有差异,但你在这里学到的内容九成在哪儿都能用。

regex 究竟是什么

正则表达式就是一段「模式」。你把模式交给搜索引擎,它会返回所有匹配该模式的文本片段。最简单的 regex 就是一个字面词:模式 cat 会找到字母 c、a、t 的连续出现。到这里都还无聊,跟普通查找替换一样。

魔法从「元字符」开始。元字符不匹配自己,而是描述类别或规则。比如点号「.」表示「任意单个字符」。模式 c.t 现在能匹配 cat、cot、cut、c5t,甚至 c#t。一个模式就抵得上几十次搜索。

整个心智模型就是这样:regex 是一段文本字面,其中某些字符被「升格」赋予了更通用的含义。学会这些被升格的字符在做什么,几乎任何模式你都能读懂、写出。

值得记住的基本积木

  • . (点)匹配除换行外的任意单个字符。
  • * 表示「前一项零次或多次」。a* 匹配空串、a、aa、aaa……
  • + 表示「前一项一次或多次」。a+ 匹配 a、aa、aaa,但不匹配空串。
  • ? 表示「零次或一次」,即可选。colou?r 同时匹配 color 和 colour。
  • ^ 把模式锚定到行首。^Hello 只在 Hello 处于行首时匹配。
  • $ 锚定到行尾。\.$ 找出所有以句号结尾的行。
  • [abc] 是字符类,匹配 a、b、c 中的一个。[a-z] 任意小写字母,[0-9] 任意数字,[A-Za-z0-9] 任意字母或数字。
  • [^abc](方括号内的脱字符)表示「除 a、b、c 之外的任意字符」。
  • \d 是任意数字的简写,\w 是任意单词字符(字母、数字、下划线),\s 是任意空白(空格、tab、换行)。它们的大写形式 \D、\W、\S 表示相反含义。
  • 圆括号 ( ) 创建一个分组,既可以让量词作用于多个字符,也可以捕获文本以便在替换中复用。
  • 竖线 | 表示「或」。cat|dog 匹配 cat 或 dog。
  • 反斜杠 \ 用于把特殊字符转义为字面匹配。要找一个真正的点,写 \.;要找字面的圆括号,写 \(。

锚点、量词和「贪婪」陷阱

锚点(^ 和 $)是写出精确模式的秘诀。没有它们,regex 在文本中飘来飘去,往往匹配比你想要的更多。加上它们,模式被钉在行首或行尾,这通常恰好就是「以数字开头的每一行」的真正含义。

量词(*、+、? 以及形如 {2,4} 的花括号)控制前一项重复的次数。要点:默认是「贪婪」的,即尽量多地匹配。如果你写 <.+> 在文本 <b>hi</b> 上跑,你以为它分别匹配 <b> 和 </b>。它实际会一口气匹配整个 <b>hi</b>,因为 + 是贪婪的。加个问号变成 <.+?>,量词就变成「懒惰」,尽量少匹配。这一招能省下大量调试时间。

用于查找替换的分组与反向引用

圆括号身兼两职。第一,它让你给一组字符加量词:(ab)+ 匹配 ab、abab、ababab。第二,也是更有用的——它把所匹配的内容捕获到一个带编号的槽里,供你在替换框中引用。第一个圆括号在多数编辑器里写作 $1(部分写作 \1),第二个是 $2,依此类推。

这就是文本重排的方法。假设你的电子表格里名字写成 Last, First,要改成 First Last。在查找框写 ([A-Za-z]+), ([A-Za-z]+),在替换框写 $2 $1。一次操作,几千行搞定。后文几乎所有配方都用这一招,所以请熟悉这个思路:左边写圆括号、右边用美元加数字。

配方 1:从混乱文本中抽出所有邮箱

邮箱在导出、签名和 CRM 转储里到处都是。下面这条模式是合理的默认。它不是 RFC 完美匹配(任何 regex 都不是),但能涵盖普通文档里所有现实的邮箱。

[\w.+-]+@[\w-]+\.[\w.-]+

配方 2:匹配各种常见格式的电话号码

电话号码是展示 regex 如何驯服脏数据的绝佳例子。下面这条能匹配 555-123-4567、(555) 123 4567、+1 555.123.4567 及大多数变体。如果只要本地号码,去掉前面的 + 和国家码即可。

\+?\d{1,3}?[\s.-]?\(?\d{2,4}\)?[\s.-]?\d{3,4}[\s.-]?\d{3,4}

配方 3:把日期从 YYYY-MM-DD 改成 DD/MM/YYYY

这是分组+反向引用的经典套路。捕获日期的每一段,再在替换框里重新排列。在查找框粘下面的模式,在替换框粘 $3/$2/$1。

(\d{4})-(\d{2})-(\d{2})

配方 4:去掉文档里所有空行

粘贴的长文本经常带着多余空行,把页数翻倍甚至翻三倍。下面的模式匹配只含可选空白和换行的行。把替换框留空,文档就会塌缩成单倍行距。

^\s*\n

配方 5:把双倍、三倍乃至更多的空格压成一个

OCR 导出、复制的 PDF 和老 Word 文档里,词与词之间常被洒入双空格。找两个或更多连续空格,替换为一个。

 {2,}

配方 6:从一段文本里抽出所有 URL

想从一篇文章、邮件导出或聊天记录里把所有链接捞出来?下面这条匹配 http 和 https URL,包含路径和查询串。配合编辑器的「复制所有匹配」功能,或者把捕获结果粘进新文档。

https?://[\w.-]+(?:/[\w./?=&%#-]*)?

配方 7:把一列名字的首字母全部大写

如果你有一列全小写的名字(jane doe、mark twain),可以两步搞定。先匹配下面的模式以捕获每个单词的首字母。多数编辑器允许在替换框里写 \u$1(把第一组捕获大写)。如果你的不行,把列表过一道大小写转换工具再贴回来——Multilities 的小型大小写转换器一键就能完成。

\b([a-z])

配方 8:只保留含数字的行

有些编辑器允许你删除所有不匹配某个 regex 的行。结合下面的模式,你可以把一份长长的日志或笔记缩减为只含数字的行——清理从 PDF 粘出的银行流水时尤其好用。

^.*\d+.*$

配方 9:清理每行末尾的空白

行尾空格肉眼不可见,却会破坏 diff 工具、邮件签名和代码注释。匹配它们并替换为空。下面的模式匹配紧贴行尾的一个或多个空格或 tab。

[ \t]+$

配方 10:从社交媒体导出里抽出所有话题标签

市场团队会把推文、LinkedIn 帖子、Instagram 文案导出,然后想数数哪些标签出现过。下面这条匹配 # 后紧跟一个或多个单词字符,能捞出所有现实的话题标签,又不会把周围的标点带进来。

#\w+

如何在你自己的工具里试这些

在 Google Docs,打开「编辑」→「查找和替换」,勾选「使用正则表达式匹配」。在 Microsoft Word,打开「查找和替换」对话框、展开高级选项、勾选「使用通配符」(Word 的通配符方言相似但不完全相同,上面的模式可能要稍微调整)。在 Notion,部分数据库属性和集成里支持 regex,但顶部查找栏本身是字面匹配。

在 Google Sheets,把模式包进 REGEXMATCH、REGEXEXTRACT 或 REGEXREPLACE:=REGEXEXTRACT(A2, "[\w.+-]+@[\w-]+\.[\w.-]+") 会从 A2 里把邮箱抽出来。Excel 较新的 REGEXEXTRACT、REGEXREPLACE、REGEXTEST 函数行为类似。在 VS Code、Sublime 和多数代码编辑器里,点击搜索栏中的 .* 图标即可切到 regex 模式。

当一个模式行为不对,不要盯着它看。把它丢进一个能边输入边高亮匹配的 regex 测试器。Multilities 提供免费的 /tools/regex-tester 正是为此而生,还有 /tools/find-replace 让你在不动原文档的前提下,对粘贴的一段文本跑模式。在测试器里迭代是秒级,在 Word 里靠撤销迭代是分钟级。

常见错误以及如何避免

  • 忘了转义点号。如果你想匹配字面句号(域名、文件扩展名、句末),写 \.;裸点号会匹配任何字符。
  • 本想用 \S+ 或 [^,]+ 时却写成 .*。点星组合是贪婪的,常常把逗号、引号或 HTML 标签都吃进去。
  • 用了 ^ 和 $ 却忘了它们在不同工具里的语义不同——有的按行、有的按整段。如果匹配出乎意料,检查编辑器是否有「multiline」或「dotall」开关。
  • 混用方言。JavaScript、Python、PCRE、Word 通配符、POSIX regex 共享多数语法,但在 lookaround、命名分组等边缘特性上有差异。换个应用就坏掉,多半是方言问题。
  • 替换时不用括号。如果你想保留所匹配内容的一部分,必须捕获它。没有括号就没法用 $1 或 \1 引用任何东西。

一个让你越用越快的小流程

需要清洗文档时,不要先去写完美的 regex。先选两三行示例文本,粘进 regex 测试器,敲出其中一行的字面版本,然后逐块「升级」:把数字换成 \d+,把变量名换成 \w+,把允许变化的标点换成字符类。每升级一次,确认测试文本仍能匹配,并且没开始误抓你不想要的内容。

模式调好后,再复制到真实文档的查找框里跑一次替换看看是否依然正确。如果文档很重要,先在副本上工作并大胆使用「撤销」。这样做上两三次,你会在一周内开始凭记忆写出最常用的清洗模式,下次遇到混乱导出时,第一反应就不再是手动点击点击点击,而是一个一气呵成的小模式。

下一步往哪儿走

上面的模式与配方足以覆盖绝大多数日常文本清洗、抽取与重排的场景。当你想再深入一些,可以查阅向前/向后断言((?=...) 与 (?<=...))、非捕获分组((?:...))和命名捕获((?<year>\d{4}))。它们对日常工作并非必须,但在面对真正棘手的文本时能帮你写出更优雅的模式。

Regex 的回报来自于短而频繁的练习,胜过任何其他技术技能。维护一个笔记文件,写下你最常用的五条配方,遇到新的就贴进去,几个月后你就会成为办公室里电子表格出乱子时大家都来求助的那个人。这是一个出乎意料地舒服的位置。

试试这些工具