💡 Key Takeaways
- What Regular Expressions Actually Are (And Why You Should Care)
- The Building Blocks: Characters, Quantifiers, and Character Classes
- Capturing Groups and Backreferences: Extracting What You Need
- Lookaheads and Lookbehinds: Advanced Pattern Matching
我仍然记得我花了六小时手动清理一个包含50,000个客户电子邮件地址的数据集的那天。那是在2012年,我是一家中型电子商务公司的初级数据分析师,当时我还不知道正则表达式。我复制、粘贴、查找、替换,骂着过了一篇又一篇的电子表格。大约在第四个小时,我的经理走过来,问我在做什么。当我解释时,她笑了——并不恶意——说:“你知道正则表达式可以在大约三十秒内完成这件事,对吧?”
💡 关键要点
- 正则表达式是什么(以及你为何需要关注它)
- 基础元素:字符、量词和字符类
- 捕获组和反向引用:提取你需要的内容
- 前瞻和后顾:高级模式匹配
那一刻改变了我的职业生涯。十二年后,作为一名处理了数十亿记录的高级数据工程师,我可以自信地说,正则表达式是数据工作中被低估的唯一技能。它们并不性感。它们没有像机器学习或区块链那样上头条。但它们是你花费下午时间在无聊的手动工作与花时间解决实际问题之间的区别。
本教程不是关于记忆晦涩的语法或一夜之间成为正则表达式大师。这是关于理解能够每周节省你数小时的实用模式。我将向你展示我最常用的确切表达式,解释它们为何有效,并给你真实的场景,展示它们如何拯救我参与的项目。到最后,你将拥有一个工具包,使你在文本处理、数据清理和验证上显著更高效。
正则表达式是什么(以及你为何需要关注它)
正则表达式——简而言之为regex——是描述文本的模式。把它们想象成一种比文本编辑器中的简单“查找”功能强大的搜索语言。与其搜索“[email protected]”这样的精确匹配,不如搜索“看起来像电子邮件地址的任何东西”这样的模式。
这在实际中有何重要性:在我目前的角色中,我经常处理包含数百万条记录的日志文件。上个月,我需要从一个2.3GB的服务器日志中提取所有IP地址,以分析流量模式。如果没有正则表达式,我需要写一个自定义解析器,可能需要50-100行代码,并小心处理边缘情况。而使用正则表达式,我只需要一行:\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b。执行时间:4.7秒。
商业影响是真实的。一个金融服务公司的同事曾告诉我,他们手动审核交易描述以对费用进行分类——每天大约200笔交易,差不多需要45分钟。我帮助他们编写了三个正则表达式模式,自动化了87%的分类。这每天节省了39分钟,或者一年大约140个小时。这个数字如果在一个团队中相乘,你就谈论真实的金钱。
正则表达式几乎在所有的编程语言和许多你已经使用的工具中都适用。Python、JavaScript、Java、C#、Ruby、PHP——它们都有正则表达式的支持。甚至Excel也通过其更新的函数有有限的正则表达式功能。文本编辑器如VS Code、Sublime Text和Vim使用正则表达式进行查找和替换。命令行工具如grep、sed和awk都是围绕正则表达式构建的。学一次,随处可用。
学习曲线确实存在,我不会骗你。正则表达式的语法乍一看令人畏惧。但根据我对数十名初级工程师的培训经验:你并不需要掌握所有内容。大约80%的实际正则表达式工作只使用可用特性的约20%。专注于这些核心模式,你会处理绝大多数的现实场景。
基础元素:字符、量词和字符类
让我们从基本概念开始。在正则表达式中,大多数字符字面上匹配它们自己。模式cat在文本中匹配单词“cat”。这很简单。但当你使用匹配模式而不是字面文本的特殊字符时,正则表达式变得强大。
"正则表达式是花六小时进行手动数据清理与花三十秒编写一个完美每次都能做到的模式之间的区别."
点号(.)是你的第一个特殊字符。它匹配除了换行符以外的任何单个字符。因此c.t匹配“cat”,“cot”,“cut”,甚至“c9t”。当我知道数据的结构但不知道确切内容时,我常常使用这个。例如,当解析遵循“AB-1234-XY”模式的产品代码时,我可能会使用..-.{4}-..来匹配任何具有该结构的代码。
量词告诉正则表达式某些内容应该出现多少次。星号(*)表示“零次或多次”,加号(+)表示“一次或多次”,而问号(?)表示“零次或一次”。这里有一个实际的例子:我曾经需要清理以各种格式出现的电话号码——有的带括号,有的带破折号,有的带空格。模式\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}处理了所有变体。问号使得括号和分隔符变为可选。
字符类让你匹配特定的字符集。方括号定义一个类:[aeiou]匹配任何元音。你可以使用范围:[a-z]匹配任何小写字母,[0-9]匹配任何数字。我经常使用[A-Za-z0-9]进行字母数字验证。还有一些简写类:\d表示数字,\w表示单词字符(字母、数字、下划线),以及\s表示空白字符。
这里有一个去年的真实场景:我在处理调查响应时,人们以极不一致的格式输入年龄——“25”,“25岁”,“25岁”,“二十五”,等等。对于数字条目,\d{1,3}\s*(years?|yrs?)?捕获了大多数变体。\d{1,3}匹配一到三位数字,\s*匹配可选的空格,带有竖线(|)的括号创建了一个可选分组,匹配“年”、“岁”、“yr”或“yrs”。
锚点对于精确匹配至关重要。脱字符(^)匹配行的开始,而美元符号($)匹配行的结束。如果没有锚点,\d{3}将在“abc123def”中的任何位置匹配“123”。有了锚点,^\d{3}$ 仅在整行恰好是三位数字时匹配。经历过这一步的艰辛,我在验证用户输入时学会了这一点——如果没有锚点,我的“三位数字代码”验证器接受了“abc123def456”,因为它在某处找到了三位数字。
捕获组和反向引用:提取你需要的内容
正则表达式中的括号不仅用于分组选择——它们还捕获匹配的文本以供后续使用。这就是正则表达式从“查找模式”转变为“提取和转换数据”的地方。我在大约60%的正则表达式工作中使用了捕获组。
| 方法 | 所需时间 | 错误率 | 可扩展性 |
|---|---|---|---|
| 手动查找/替换 | 小时到天 | 高(人力疲惫) | 差(不具可扩展性) |
| 基本字符串方法 | 分钟到小时 | 中等(有限模式) | 适中(仅适用简单情况) |
| 正则表达式 | 秒到分钟 | 低(逻辑一致) | 优秀(可处理数百万条) |
| 自定义解析器脚本 | 编写数小时 | 低(如果经过良好测试) | 良好(但维护繁重) |
假设你有格式为“2024-03-15”的日期,并需要将其转换为“03/15/2024”。模式(\d{4})-(\d{2})-(\d{2})创建了三个捕获组。在大多数编程语言中,你可以引用这些捕获:组1是年份,组2是月份,组3是日期。然后你可以重新排列它们:在替换字符串中使用$2/$3/$1即可获得新的格式。
我最近使用这个技巧处理了18,000个需要重新格式化的产品描述。原始格式是“ProductName (SKU: 12345) - $99.99”,我们需要“12345 | ProductName | $99.99”。模式(.+?) \(SKU: (\d+)\) - (\$[\d.]+)捕获了三个组成部分,替换$2 | $1 | $3对它们进行了重新排列。总时间:大约90秒来编写和测试正则表达式,2.3秒处理所有记录。
非捕获组在你需要分组以进行选择或量词但不想捕获文本时非常有用。使用(?:...)代替(...)。例如,(?:Mr|Ms|Mrs)\. ([A-Z][a-z]+)匹配头衔,但仅捕获名字。这确保你的捕获组编号合理,并且在大型数据集上可以略微提高性能。
反向引用允许你匹配模式中之前捕获的相同文本。语法是<