正则表达式:开发者实用指南
· 12分钟阅读
正则表达式(regex)是开发者工具箱中最强大的工具之一。它们提供了一种简洁、灵活的方式,使用模式描述来搜索、匹配和操作文本。无论您是在验证用户输入、解析日志文件、从API提取数据,还是执行复杂的查找和替换操作,正则表达式知识对于高效开发都是必不可少的。
这份综合指南将带您从基础知识到高级模式,并提供实用的真实示例。最后,您不仅会理解语法,还会知道何时以及如何在项目中有效应用正则表达式。
正则表达式基础
从本质上讲,正则表达式是定义搜索模式的字符序列。可以将其视为描述文本模式的迷你语言。让我们从构成每个正则表达式模式基础的基本构建块开始。
字面字符
最简单的正则表达式是字面字符串。模式hello匹配输入中的确切文本"hello"。大多数字符按字面意思匹配自己,使基本搜索变得简单直接。
但是,某些字符在正则表达式中具有特殊含义,称为元字符。当您想要按字面意思匹配它们时,必须使用反斜杠进行转义:
. ^ $ * + ? { } [ ] \ | ( )
例如,要匹配字面句点,使用\.而不是仅使用.。要匹配美元符号,使用\$。
锚点
锚点不匹配字符——它们匹配文本中的位置。它们对于精确的模式匹配至关重要:
^— 匹配行或字符串的开头$— 匹配行或字符串的结尾\b— 单词边界(单词字符和非单词字符之间)\B— 非单词边界(\b的相反)\A— 字符串开头(在某些版本中,在多行模式下与^不同)\Z— 字符串结尾(在某些版本中,在多行模式下与$不同)
示例:模式^Hello$仅匹配恰好包含"Hello"且前后没有其他文本的行。
示例:模式\bcat\b匹配作为完整单词的"cat",但不匹配"category"或"concatenate"中的"cat"。
专业提示:使用锚点防止部分匹配。在验证电子邮件地址或电话号码时,始终使用^和$锚定您的模式,以确保整个字符串匹配您的模式,而不仅仅是其中的一部分。
字符类和量词
字符类和量词是正则表达式真正强大的地方。它们允许您匹配字符范围并指定模式应重复多少次。
字符类
字符类匹配定义集合中的任何一个字符。它们用方括号括起来:
[abc]— 匹配a、b或c[a-z]— 匹配任何小写字母[A-Z0-9]— 匹配任何大写字母或数字[^abc]— 匹配除了a、b或c之外的任何字符(否定类).— 匹配除换行符外的任何字符
您可以组合多个范围:[a-zA-Z0-9]匹配任何字母数字字符。
简写字符类
这些预定义类节省时间并提高可读性:
| 简写 | 等效 | 描述 |
|---|---|---|
\d |
[0-9] |
任何数字 |
\D |
[^0-9] |
任何非数字 |
\w |
[a-zA-Z0-9_] |
任何单词字符(字母、数字、下划线) |
\W |
[^a-zA-Z0-9_] |
任何非单词字符 |
\s |
[ \t\n\r\f\v] |
任何空白字符 |
\S |
[^ \t\n\r\f\v] |
任何非空白字符 |
量词
量词指定前面的元素应匹配多少次:
*— 零次或多次(贪婪)+— 一次或多次(贪婪)?— 零次或一次(可选){n}— 恰好n次{n,}— 至少n次{n,m}— n到m次之间(包含)
示例:\d{3}-\d{2}-\d{4}匹配社会安全号码格式,如"123-45-6789"。
示例:colou?r同时匹配"color"和"colour"('u'是可选的)。
贪婪与懒惰量词
默认情况下,量词是贪婪的——它们匹配尽可能多的文本。在量词后添加?使其变为懒惰(非贪婪),匹配尽可能少的文本:
*?— 零次或多次(懒惰)+?— 一次或多次(懒惰)??— 零次或一次(懒惰){n,m}?— n到m次之间(懒惰)
示例:给定文本<div>content</div>,模式<.+>(贪婪)匹配整个字符串,而<.+?>(懒惰)仅匹配<div>。
快速提示:在提取分隔符之间的内容(如HTML标签或引号)时,始终使用懒惰量词以避免匹配过多。模式".*?"正确提取带引号的字符串,而".*"会从整行中的第一个引号匹配到最后一个引号。
分组、捕获和反向引用
分组允许您将多个字符视为单个单元,并捕获匹配的文本以供以后使用。它们对于复杂模式和文本提取至关重要。
捕获组
括号()创建捕获组。匹配的文本被存储并可以稍后引用:
(\d{3})-(\d{3})-(\d{4})
此模式匹配电话号码并捕获三个组:区号、前缀和线路号码。在大多数语言中,您可以将这些捕获作为$1、$2、$3或类似语法访问。
非捕获组
有时您需要分组用于量词或交替,但不需要捕获文本。使用(?:...)表示非捕获组:
(?:https?|ftp)://[^\s]+
这匹配以http、https或ftp开头的URL,而不单独捕获协议。当您不需要捕获的文本时,非捕获组更高效。
命名捕获组
命名组使您的正则表达式更具可读性和可维护性。语法因语言而异:
- Python、.NET、PCRE:
(?P<name>...)或(?<name>...) - JavaScript(ES2018+):
(?<name>...)
示例:
(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})
这匹配日期并为年、月、日创建命名捕获,使您的代码更具自我说明性。
反向引用
反向引用允许您匹配模式中先前捕获的相同文本:
\1、\2等 — 按编号引用捕获组\k<name>— 引用命名组(语法各异)
示例:\b(\w+)\s+\1\b匹配重复的单词,如"the the"或"is is"。
示例:(['"])(.*?)\1匹配单引号和双引号字符串,确保结束引号与开始引号匹配。
交替
管道字符|充当OR运算符。它通常与组一起使用:
(cat|dog|bird)
这匹配"cat"、"dog"或"bird"。注意交替顺序——正则表达式引擎从左到右尝试替代项,并在第一次匹配时停止。
前瞻和后顾断言
环视断言是零宽度断言,根据前后内容匹配位置,而不将该文本包含在匹配中。它们对于复杂的匹配场景非常强大。
前瞻断言
前瞻检查当前位置之后的内容:
(?=...)— 正向前瞻(必须后跟...)(?!...)— 负向前瞻(不能后跟...)
示例:\d+(?= dollars)匹配后跟" dollars"的数字,但不在匹配中包含" dollars"。
示例:^(?=.*[A-Z])(?=.*[a-z])(?=.*\d).{8,}$验证包含至少一个大写字母、一个小写字母、一个数字且至少8个字符的密码。
后顾断言
后顾检查当前位置之前的内容:
(?<=...)— 正向后顾(必须前面是...)(?<!...)— 负向后顾(不能前面是...)
示例:(?<=\$)\d+匹配前面有美元符号的数字,但不在匹配中包含美元符号。
示例:(?<!un)happy匹配"happy"但不匹配"unhappy"。
专业提示:环视断言非常适合验证场景,您需要检查多个条件而不消耗字符。当您需要根据上下文匹配某些内容但只想提取特定部分时,它们也是必不可少的。
实用环视示例
从电子邮件中提取域名而不包含@符号:
(?<=@)[a-zA-Z0-9.-]+
匹配后面没有逗号的单词:
\b\w+\b(?!,)
查找前面没有减号的数字(仅正数):
(?<!-)\b\d+\b
常见模式和实际示例
让我们探索经过实战检验的常见开发任务正则表达式模式。这些模式已准备好用于生产,并处理您在实际应用程序中会遇到的边缘情况。
电子邮件验证
一个在严格性和实际使用之间取得平衡的实用电子邮件模式:
^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$
此模式允许常见的电子邮件格式,同时防止明显错误。对于符合RFC的验证,请考虑使用专用库——完整的RFC 5322正则表达式超过6,000个字符。
URL匹配
匹配带有可选www的HTTP/HTTPS URL:
https?://(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)
对于捕获大多数URL的更简单版本:
https?://[^\s]+
电话号码
具有灵活格式的美国电话号码:
^(\+1[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}$
这匹配以下格式:
- 123-456-7890
- (123) 456-7890
- +1 123 456 7890
- 1234567890
日期格式
ISO 8601日期格式(YYYY-MM-DD):
^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$
美国日期格式(MM/DD/YYYY):
^(0[1-9]|1[0-2])/(0[1-9]|[12]\d|3[01])/\d{4}$
IP地址
IPv4地址验证:
^((25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\.){3}(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)$
这确保每个八位字节在0到255之间。
信用卡号码
基本信用卡格式(带有可选空格或破折号):
^\d{4}[-\s]?\d{4}[-\s]?\d{4}[-\s]?\d{4}$
请记住使用Luhn算法进行实际验证——正则表达式仅检查格式。
十六进制颜色
匹配带有可选#前缀的十六进制颜色代码:
^#?([a-fA-F0-9]{6}|[a-fA-F0-9]{3})$
这匹配6位(#FF5733)和3位(#F57)格式。
用户名验证
带有下划线和连字符的字母数字用户名,3-16个字符:
^[a-zA-Z0-9_-]{3,16}$
密码强度
要求至少8个字符、一个大写字母、一个小写字母、一个数字和一个特殊字符:
^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&