深入浅出正则表达式:从基础到高级验证技巧


深入浅出正则表达式:从基础到高级验证技巧

正则表达式(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 分组和捕获

圆括号 () 用于将一部分表达式组合起来,形成一个子表达式。分组有以下几个作用:

  1. 分组操作:可以将量词应用于整个分组。例如,(ab)+ 匹配 "ab"、"abab"、"ababab" 等。
  2. 捕获:正则表达式引擎会记住每个分组匹配的内容,可以通过编号或命名来引用这些捕获的内容。
    • 编号捕获:从左到右,按照左括号出现的顺序,从 1 开始依次编号。例如,(ab)(cd) 中,第一个分组是 (ab),第二个分组是 (cd)
    • 命名捕获:使用 (?<name>...) 的语法为分组命名。例如,(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2}) 将日期中的年、月、日分别捕获到名为 "year"、"month" 和 "day" 的分组中。
  3. 非捕获分组:使用 (?:...) 的语法可以创建一个非捕获分组,它不会被记住,也不会分配编号。非捕获分组仅用于分组操作,不影响捕获结果。

2.2 反向引用

反向引用允许您在正则表达式中引用之前捕获的内容。使用 \n(n 是分组的编号)或 \k<name>(name 是分组的名称)来引用捕获的内容。

例如,要匹配重复的单词,可以使用 \b(\w+)\s+\1\b。其中,\b 表示单词边界,(\w+) 捕获一个单词,\s+ 匹配一个或多个空白字符,\1 反向引用第一个分组捕获的内容(即之前捕获的单词)。

2.3 零宽断言(Lookarounds)

零宽断言是一种特殊的正则表达式语法,它用于指定匹配的位置,但不会消耗字符(即不会包含在匹配结果中)。零宽断言分为四种:

  1. 正向肯定预查(Positive Lookahead)(?=pattern)

    • 断言自身出现的位置的后面能匹配 pattern 表达式。
  2. 正向否定预查(Negative Lookahead)(?!pattern)

    • 断言自身出现的位置的后面不能匹配 pattern 表达式。
  3. 反向肯定预查(Positive Lookbehind)(?<=pattern)

    • 断言自身出现的位置的前面能匹配 pattern 表达式。
    • 注意:部分正则表达式引擎不支持反向预查,或对反向预查中的模式长度有限制。
  4. 反向否定预查(Negative Lookbehind)(?<!pattern)

    • 断言自身出现的位置的前面不能匹配 pattern 表达式。
    • 注意:部分正则表达式引擎不支持反向预查,或对反向预查中的模式长度有限制。
      例如:
  5. 提取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个特殊字符

四、总结

正则表达式是一个强大的文本处理工具,掌握它可以极大地提高文本处理的效率和准确性。本文从正则表达式的基础概念出发,介绍了元字符、量词、字符类、分组、反向引用、零宽断言等核心概念,并详细讲解了贪婪与非贪婪模式、修饰符等进阶技巧。最后,通过一些常见的高级验证示例,展示了正则表达式在实际应用中的强大功能。

要熟练掌握正则表达式,需要不断练习和实践。建议您:

  • 多阅读文档:不同编程语言或工具的正则表达式实现可能略有差异,阅读相关文档可以了解具体细节。
  • 多尝试:使用在线正则表达式测试工具或编程语言中的正则表达式库,尝试编写和测试不同的正则表达式。
  • 多思考:遇到复杂的文本处理问题时,思考如何使用正则表达式来解决。
  • 多总结:将常用的正则表达式记录下来,形成自己的正则表达式库。

希望本文能帮助您深入理解正则表达式,并在实际工作中灵活运用这一强大的工具。

THE END