正则表达式:初学者指南
· 12分钟阅读
正则表达式(regex)是开发者工具库中最强大的工具之一。它们起初可能看起来令人生畏,但一旦你理解了基础知识,它们就会成为文本处理、验证和数据提取不可或缺的工具。
无论你是在验证用户输入、解析日志文件还是转换数据,正则表达式都提供了一种简洁灵活的方式来处理文本模式。本指南将带你从完全的初学者成长为自信的正则表达式用户。
什么是正则表达式?
正则表达式是定义搜索模式的字符序列。可以把它看作是描述文本模式的迷你语言——你可以搜索"任何电子邮件地址"或"任何电话号码"等模式,而不是搜索精确的字符串。
正则表达式几乎在每种编程语言和文本编辑器中都有使用。它们在JavaScript、Python、Java、PHP、Ruby、Go和无数其他语言中都得到支持。甚至像grep、sed和awk这样的命令行工具也严重依赖正则表达式。
正则表达式的美妙之处在于,一旦你学会了语法,就可以在不同的工具和语言中应用它。虽然正则表达式的不同"风格"(PCRE、JavaScript、Python等)之间存在细微差异,但核心概念保持不变。
专业提示: 从简单的模式开始,逐步增加复杂性。不要试图在第一次尝试时就写出完美的正则表达式——在测试时迭代和改进。
基本构建块
每个正则表达式模式都是由基本组件构建的。在转向更复杂的模式之前,理解这些构建块至关重要。
字面字符
最简单的正则表达式就是纯文本。模式cat匹配字符串中任何位置的确切文本"cat"。大多数字母数字字符按字面意思匹配自己。
但是,某些字符在正则表达式中具有特殊含义,需要用反斜杠转义:. ^ $ * + ? { } [ ] \ | ( )
要匹配字面句点,你需要写\.而不是只写.
点元字符
点(.)是一个通配符,匹配除换行符外的任何单个字符。模式c.t匹配"cat"、"cot"、"cut"、"c9t",甚至"c@t"。
这使得点非常强大,但如果使用不当也可能很危险。我们稍后会介绍如何使其更具体。
字符类
方括号创建一个字符类,匹配括号内的任何单个字符:
[aeiou]匹配任何元音[0-9]匹配任何数字[a-zA-Z]匹配任何字母(大写或小写)[a-z0-9]匹配任何小写字母或数字
你还可以用插入符号否定字符类:[^0-9]匹配任何不是数字的字符。
量词详解
量词指定模式应该匹配多少次。它们放在你想要重复的元素之后。
| 量词 | 含义 | 示例 |
|---|---|---|
* |
0次或多次 | ab*c 匹配"ac"、"abc"、"abbc" |
+ |
1次或多次 | ab+c 匹配"abc"、"abbc"但不匹配"ac" |
? |
0次或1次(可选) | colou?r 匹配"color"和"colour" |
{n} |
恰好n次 | \d{3} 恰好匹配3个数字 |
{n,} |
n次或更多次 | \d{2,} 匹配2个或更多数字 |
{n,m} |
n到m次之间 | \d{2,4} 匹配2、3或4个数字 |
贪婪匹配与懒惰匹配
默认情况下,量词是贪婪的——它们尽可能多地匹配文本。模式.*会消耗它能消耗的所有内容。
考虑匹配HTML标签:<.+>应用于<b>bold</b>将匹配整个字符串,而不仅仅是<b>。
要使量词变为懒惰(尽可能少地匹配),添加一个问号:<.+?>现在将分别匹配<b>和</b>。
快速提示: 如有疑问,使用懒惰量词。它们更可预测,不太可能导致意外匹配。
字符类和快捷方式
重复写[0-9]会变得乏味。正则表达式为常见模式提供了简写字符类。
| 简写 | 等效 | 描述 |
|---|---|---|
\d |
[0-9] |
任何数字 |
\D |
[^0-9] |
任何非数字 |
\w |
[a-zA-Z0-9_] |
任何单词字符 |
\W |
[^a-zA-Z0-9_] |
任何非单词字符 |
\s |
[ \t\r\n\f] |
任何空白字符 |
\S |
[^ \t\r\n\f] |
任何非空白字符 |
注意这个模式:大写版本是其小写对应版本的否定。这使得正则表达式更易读和简洁。
使用快捷方式的实际示例
\d{3}-\d{4}匹配像"555-1234"这样的电话号码\w+@\w+\.\w+匹配简单的电子邮件地址\s+匹配一个或多个空白字符\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}匹配IP地址(虽然不完美)
锚点和边界
锚点不匹配字符——它们匹配文本中的位置。它们对于精确的模式匹配至关重要。
行锚点
^匹配行的开始$匹配行的结束
模式^Hello只匹配行开头的"Hello"。类似地,world$只匹配行末尾的"world"。
要精确匹配整行,同时使用两者:^Hello world$只匹配恰好包含"Hello world"且前后没有其他内容的行。
单词边界
\b锚点匹配单词边界——单词字符(\w)和非单词字符之间的位置。
这对于匹配整个单词非常有用。模式\bcat\b匹配"cat"但不匹配"category"或"scat"。
没有单词边界,cat会匹配所有三个。单词边界使你的模式更精确而不增加复杂性。
专业提示: 搜索整个单词时始终使用单词边界。它可以防止错误匹配并使你的正则表达式更可靠。
分组和捕获
括号在正则表达式中有两个用途:分组和捕获。一旦你理解了它们的工作原理,它们就是最强大的功能之一。
用于量词的分组
括号允许你将量词应用于多个字符。模式(ha)+匹配"ha"、"haha"、"hahaha"等。
没有括号,ha+会匹配"ha"、"haa"、"haaa"——量词只应用于前面的字符。
捕获组
组还会捕获匹配的文本以供以后使用。考虑这个电话号码模式:(\d{3})-(\d{3})-(\d{4})
这创建了三个捕获组:区号、前缀和线路号码。在大多数语言中,你可以访问这些捕获:
- JavaScript:
match[1]、match[2]、match[3] - Python:
match.group(1)、match.group(2)、match.group(3) - 在替换中:
$1、$2、$3或\1、\2、\3
非捕获组
有时你想要分组而不捕获。使用(?:...)表示非捕获组:(?:https?://)?www\.example\.com
这会对协议进行分组但不创建捕获组,这可以提高性能并简化你的代码。
命名捕获组
你可以为组命名以提高清晰度,而不是使用编号组:(?<area>\d{3})-(?<prefix>\d{3})-(?<line>\d{4})
在Python中使用match.group('area')或在JavaScript中使用match.groups.area访问命名组。这使你的代码具有自我说明性。
实际示例
让我们将所学知识应用于实际场景。这些模式是起点——你通常需要根据具体要求进行调整。
电子邮件验证
一个简单的电子邮件模式:[\w.+-]+@[\w.-]+\.[a-zA-Z]{2,}
这匹配大多数常见的电子邮件格式,但不符合RFC标准。对于生产使用,考虑使用专用的电子邮件验证库——电子邮件正则表达式可能会变得极其复杂。
URL匹配
匹配HTTP和HTTPS URL:https?://[\w.-]+(?:\.[\w.-]+)+(?:/[\w./?&=%-]*)?
这处理域名、路径和查询字符串。s?使'https'中的's'可选。
电话号码
具有灵活格式的美国电话号码:\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}
这匹配如下格式:
- (555) 123-4567
- 555-123-4567
- 555.123.4567
- 5551234567
日期格式
ISO日期格式(YYYY-MM-DD):\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01])
这确保月份为01-12,日期为01-31。它比\d{4}-\d{2}-\d{2}更准确,后者会接受像2024-99-99这样的无效日期。
IP地址
IPv4地址:\b(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\b
这验证每个八位字节在0-255之间,防止匹配像999.999.999.999这样的内容。
信用卡号码
匹配带有可选空格或破折号的信用卡号码:\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}
记住使用Luhn算法单独验证校验和——仅靠正则表达式无法验证卡号是否有效。
安全提示: 切勿以明文形式记录或存储信用卡号码。仅使用这些模式进行初始格式验证,然后立即对敏感数据进行标记化。
从日志中提取数据
解析Apache日志条目: