深入浅出正则表达式:从基础到高级验证技巧
深入浅出正则表达式:从基础到高级验证技巧
正则表达式(Regular Expression,简称 Regex 或 RegExp)是计算机科学中一个强大而灵活的文本处理工具。它使用一种特定的模式描述语言来匹配、搜索、替换文本字符串。无论是简单的文本查找,还是复杂的数据验证和提取,正则表达式都能胜任。本文将带您从正则表达式的基础概念入手,逐步深入到高级验证技巧,让您全面掌握这一强大的工具。
一、正则表达式基础
1.1 什么是正则表达式?
正则表达式本质上是一个由字符和特殊符号组成的字符串,它描述了一种文本模式。这个模式可以用来:
- 验证:检查一个字符串是否符合特定的格式(例如,邮箱地址、电话号码、身份证号)。
- 搜索:在一段文本中查找符合特定模式的子字符串。
- 替换:将文本中符合特定模式的子字符串替换为其他内容。
- 提取:从文本中提取符合特定模式的子字符串。
1.2 正则表达式的组成
正则表达式由以下几个部分组成:
- 普通字符:字母、数字、汉字、下划线、以及没有特殊定义的标点符号,都是普通字符。普通字符在匹配时,匹配与之相同的一个字符。
- 元字符:具有特殊含义的字符,用于构建更复杂的匹配模式。
- 量词:指定前面的字符或子表达式出现的次数。
- 字符类:用方括号
[]
包围的一组字符,表示匹配其中任意一个字符。 - 分组和捕获:用圆括号
()
将一部分表达式组合起来,形成一个子表达式,可以对子表达式进行操作,也可以捕获子表达式匹配的内容。 - 选择:使用竖线
|
表示“或”的关系,匹配多个可能的模式。 - 锚点:指定匹配的位置,例如字符串的开头或结尾。
- 转义字符:反斜杠
\
用于转义元字符,使其失去特殊含义,变为普通字符;或者用于表示一些不可打印的字符(例如,\n
表示换行符)。
1.3 常用元字符
以下是一些常用的元字符及其含义:
元字符 | 含义 |
---|---|
. |
匹配除换行符以外的任意单个字符。 |
^ |
匹配字符串的开头。 |
$ |
匹配字符串的结尾。 |
* |
匹配前面的字符或子表达式零次或多次。 |
+ |
匹配前面的字符或子表达式一次或多次。 |
? |
匹配前面的字符或子表达式零次或一次。 |
{n} |
匹配前面的字符或子表达式恰好 n 次。 |
{n,} |
匹配前面的字符或子表达式至少 n 次。 |
{n,m} |
匹配前面的字符或子表达式至少 n 次,但不超过 m 次。 |
[] |
字符类,匹配方括号中任意一个字符。 |
[^] |
否定字符类,匹配不在方括号中的任意一个字符。 |
() |
分组,将括号内的表达式作为一个整体,也可以用于捕获匹配的内容。 |
| |
选择,匹配竖线两侧的任意一个表达式。 |
\ |
转义字符,用于转义元字符或表示特殊字符。 |
1.4 常用字符类
字符类 | 含义 |
---|---|
[abc] |
匹配 a、b 或 c 中的任意一个字符。 |
[a-z] |
匹配任意小写字母。 |
[A-Z] |
匹配任意大写字母。 |
[0-9] |
匹配任意数字。 |
[a-zA-Z] |
匹配任意字母(不区分大小写)。 |
[0-9a-zA-Z] |
匹配任意数字或字母。 |
[^abc] |
匹配除 a、b、c 以外的任意字符。 |
1.5 常用转义字符
转义字符 | 含义 |
---|---|
\d |
匹配任意数字,等价于 [0-9] 。 |
\D |
匹配任意非数字字符,等价于[^0-9] 。 |
\w |
匹配任意字母、数字或下划线,等价于 [a-zA-Z0-9_] 。 |
\W |
匹配任意非字母、数字或下划线,等价于 [^a-zA-Z0-9_] 。 |
\s |
匹配任意空白字符,包括空格、制表符、换行符等。 |
\S |
匹配任意非空白字符。 |
\n |
匹配换行符。 |
\r |
匹配回车符。 |
\t |
匹配制表符。 |
1.6 量词详解
*
:匹配前面的字符或子表达式零次或多次。例如,ab*
可以匹配 "a"、"ab"、"abb"、"abbb" 等。+
:匹配前面的字符或子表达式一次或多次。例如,ab+
可以匹配 "ab"、"abb"、"abbb" 等,但不能匹配 "a"。?
:匹配前面的字符或子表达式零次或一次。例如,ab?
可以匹配 "a" 或 "ab"。{n}
:匹配前面的字符或子表达式恰好 n 次。例如,a{3}
匹配 "aaa"。{n,}
:匹配前面的字符或子表达式至少 n 次。例如,a{2,}
匹配 "aa"、"aaa"、"aaaa" 等。{n,m}
:匹配前面的字符或子表达式至少 n 次,但不超过 m 次。例如,a{2,4}
匹配 "aa"、"aaa" 或 "aaaa"。
二、正则表达式进阶
2.1 分组和捕获
圆括号 ()
用于将一部分表达式组合起来,形成一个子表达式。分组有以下几个作用:
- 分组操作:可以将量词应用于整个分组。例如,
(ab)+
匹配 "ab"、"abab"、"ababab" 等。 - 捕获:正则表达式引擎会记住每个分组匹配的内容,可以通过编号或命名来引用这些捕获的内容。
- 编号捕获:从左到右,按照左括号出现的顺序,从 1 开始依次编号。例如,
(ab)(cd)
中,第一个分组是(ab)
,第二个分组是(cd)
。 - 命名捕获:使用
(?<name>...)
的语法为分组命名。例如,(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})
将日期中的年、月、日分别捕获到名为 "year"、"month" 和 "day" 的分组中。
- 编号捕获:从左到右,按照左括号出现的顺序,从 1 开始依次编号。例如,
- 非捕获分组:使用
(?:...)
的语法可以创建一个非捕获分组,它不会被记住,也不会分配编号。非捕获分组仅用于分组操作,不影响捕获结果。
2.2 反向引用
反向引用允许您在正则表达式中引用之前捕获的内容。使用 \n
(n 是分组的编号)或 \k<name>
(name 是分组的名称)来引用捕获的内容。
例如,要匹配重复的单词,可以使用 \b(\w+)\s+\1\b
。其中,\b
表示单词边界,(\w+)
捕获一个单词,\s+
匹配一个或多个空白字符,\1
反向引用第一个分组捕获的内容(即之前捕获的单词)。
2.3 零宽断言(Lookarounds)
零宽断言是一种特殊的正则表达式语法,它用于指定匹配的位置,但不会消耗字符(即不会包含在匹配结果中)。零宽断言分为四种:
-
正向肯定预查(Positive Lookahead):
(?=pattern)
- 断言自身出现的位置的后面能匹配
pattern
表达式。
- 断言自身出现的位置的后面能匹配
-
正向否定预查(Negative Lookahead):
(?!pattern)
- 断言自身出现的位置的后面不能匹配
pattern
表达式。
- 断言自身出现的位置的后面不能匹配
-
反向肯定预查(Positive Lookbehind):
(?<=pattern)
- 断言自身出现的位置的前面能匹配
pattern
表达式。 - 注意:部分正则表达式引擎不支持反向预查,或对反向预查中的模式长度有限制。
- 断言自身出现的位置的前面能匹配
-
反向否定预查(Negative Lookbehind):
(?<!pattern)
- 断言自身出现的位置的前面不能匹配
pattern
表达式。 - 注意:部分正则表达式引擎不支持反向预查,或对反向预查中的模式长度有限制。
例如:
- 断言自身出现的位置的前面不能匹配
- 提取html中
<p></p>
标签内的文字内容
(?<=<p>).*?(?=</p>)
2.4 贪婪与非贪婪模式
默认情况下,正则表达式的量词(*
、+
、?
、{n,}
、{n,m}
)是贪婪的,它们会尽可能多地匹配字符。在量词后面加上一个问号 ?
,可以将其变为非贪婪模式,使其尽可能少地匹配字符。
例如,对于字符串 "abbbbbc",正则表达式 ab+
(贪婪模式)会匹配 "abbbbb",而 ab+?
(非贪婪模式)会匹配 "ab"。
2.5 修饰符(Flags)
修饰符用于改变正则表达式的行为。常见的修饰符有:
i
(忽略大小写):使匹配不区分大小写。g
(全局匹配):查找所有匹配项,而不是只找到第一个匹配项。m
(多行模式):使^
和$
匹配每一行的开头和结尾,而不仅仅是整个字符串的开头和结尾。s
(dotall 模式): 使.
可以匹配包括换行符在内的任意字符。
不同的编程语言或工具中,修饰符的写法可能略有不同。例如,在 JavaScript 中,可以将修饰符放在正则表达式的末尾,如 /pattern/igm
。
三、高级验证技巧
3.1 邮箱地址验证
regex
^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$
更严格的:
regex
^[\w!#$%&'*+/=?`{|}~^-]+(?:\.[\w!#$%&'*+/=?`{|}~^-]+)*@(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,6}$
^
和$
:匹配字符串的开头和结尾。[a-zA-Z0-9._%+-]+
:匹配用户名部分,允许字母、数字、点、下划线、百分号、加号和减号。@
:匹配 "@" 符号。[a-zA-Z0-9.-]+
:匹配域名部分,允许字母、数字、点和减号。\.
:匹配点号。[a-zA-Z]{2,}
:匹配顶级域名,至少两个字母。
3.2 电话号码验证(中国大陆)
regex
^1[3456789]\d{9}$
^
和$
:匹配字符串的开头和结尾。1
:匹配数字 1。[3456789]
:匹配第二位数字,可以是 3、4、5、6、7、8、9 中的任意一个。\d{9}
:匹配任意 9 个数字。
3.3 身份证号码验证(中国大陆)
regex
(^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$)|(^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{2}[0-9Xx]$)
- 这个正则表达式比较复杂,主要是对身份证号码的组成规则进行了详细的拆分和匹配。
- 可以根据需要进行简化或调整。
3.4 URL 验证
regex
^(https?|ftp):\/\/[^\s/$.?#].[^\s]*$
^
和$
:匹配字符串的开头和结尾。(https?|ftp)
:匹配 "http"、"https" 或 "ftp"。:\/\/
:匹配 "://"。[^\s/$.?#]
:匹配除空白字符、斜杠、点、问号、井号以外的任意字符。[^\s]*
:匹配除空白字符以外的任意字符,零次或多次。
更严谨的:
regex
^(?:(?:https?|ftp):\/\/)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/\S*)?$
3.5 IP 地址验证
regex
^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$
^
和$
:匹配字符串的开头和结尾。((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}
:匹配前三个数字段,每个字段可以是 0-255。(25[0-5]|2[0-4]\d|[01]?\d\d?)
:匹配最后一个数字段,可以是 0-255。
3.6 日期验证 (YYYY-MM-DD)
regex
^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$
^
和$
:匹配字符串的开头和结尾。\d{4}
:匹配四位数字年份。(0[1-9]|1[0-2])
:匹配月份,可以是 01-09 或 10-12。(0[1-9]|[12]\d|3[01])
:匹配日期,可以是 01-09、10-29 或 30-31。
更严格的日期验证,需要结合具体月份和闰年来判断。
3.7 强密码验证
^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$
至少8个字符,至少1个大写字母,1个小写字母,1个数字和1个特殊字符
四、总结
正则表达式是一个强大的文本处理工具,掌握它可以极大地提高文本处理的效率和准确性。本文从正则表达式的基础概念出发,介绍了元字符、量词、字符类、分组、反向引用、零宽断言等核心概念,并详细讲解了贪婪与非贪婪模式、修饰符等进阶技巧。最后,通过一些常见的高级验证示例,展示了正则表达式在实际应用中的强大功能。
要熟练掌握正则表达式,需要不断练习和实践。建议您:
- 多阅读文档:不同编程语言或工具的正则表达式实现可能略有差异,阅读相关文档可以了解具体细节。
- 多尝试:使用在线正则表达式测试工具或编程语言中的正则表达式库,尝试编写和测试不同的正则表达式。
- 多思考:遇到复杂的文本处理问题时,思考如何使用正则表达式来解决。
- 多总结:将常用的正则表达式记录下来,形成自己的正则表达式库。
希望本文能帮助您深入理解正则表达式,并在实际工作中灵活运用这一强大的工具。