正则表达式

一、概述

正则表达式(Regular Expression)是一种用于匹配文本的模式,广泛应用于文本搜索、替换、数据验证等领域。它通过定义规则来描述字符串的格式和内容,可以匹配复杂的文本结构。

二、匹配单个字符

在正则表达式中,匹配单个字符是构建复杂模式的基础。主要包括普通字符特殊字符(如点号 .)。以下是对这两种匹配单个字符方式的详细说明:

1. 普通字符

定义:普通字符是指除正则表达式元字符(如 ., *, +, ?, ^, $, [], (), {}, |, \ 等)之外的所有字符。它们在正则表达式中代表自身,直接用于匹配文本中的相应字符。

用法: - 直接匹配:普通字符直接在正则表达式中出现,表示要匹配该字符本身。

示例: - 匹配单个字符: - 正则表达式:a - 匹配字符串:"apple" 中的第一个 "a"

  • 匹配多个普通字符
    • 正则表达式:cat
    • 匹配字符串:"The cat sat on the mat." 中的 "cat"
  • 匹配带有特殊意义的字符
    • 如果需要匹配元字符本身,如 .*,必须使用转义字符 \
    • 例如,匹配句点 .
      • 正则表达式:\.
      • 匹配字符串:"End of sentence." 中的句点。

注意事项: - 区分大小写:默认情况下,正则表达式是区分大小写的。例如,a 不匹配 A。可以使用标志(如 i)来忽略大小写。 - 转义字符:当普通字符与元字符冲突时,需要使用反斜杠 \ 进行转义。例如,要匹配字符 *,应使用 \*

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import re

# 匹配单个 'a'
pattern = r'a'
text = 'apple'
match = re.search(pattern, text)
print(match.group()) # 输出: a

# 匹配 'cat'
pattern = r'cat'
text = 'The cat sat on the mat.'
match = re.search(pattern, text)
print(match.group()) # 输出: cat

# 匹配句点 '.'
pattern = r'\.'
text = 'End of sentence.'
match = re.search(pattern, text)
print(match.group()) # 输出: .

2. 点号(.

定义:点号 . 是一个特殊的正则表达式元字符,用于匹配除换行符(\n)之外的任意单个字符。

用法

  • 通配符:在正则表达式中使用 . 可以匹配任何字符(除换行符),常用于模糊匹配。

示例: - 基本用法: - 正则表达式:a.b - 匹配字符串:"aab", "acb", "a3b" 等,匹配 "a" 后跟任意一个字符,再跟 "b"

  • 多重通配
    • 正则表达式:...
    • 匹配任何三个连续的字符,如 "abc", "123", "a b"
  • 与重复符结合
    • 正则表达式:a.*b
    • 匹配以 "a" 开头,以 "b" 结尾,中间可以有任意数量的任意字符,如 "ab", "a123b", "a!@#b"

高级用法

  • 匹配换行符

    • 默认情况下,. 不匹配换行符。如果需要匹配包括换行符在内的任意字符,可以使用 单行模式re.DOTALLs 标志)。

    示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import re

    # 默认情况下,'.' 不匹配换行符
    pattern = r'a.b'
    text = 'a\nb'
    match = re.search(pattern, text)
    print(match) # 输出: None

    # 使用 re.DOTALL 使 '.' 匹配换行符
    match = re.search(pattern, text, re.DOTALL)
    if match:
    print(match.group()) # 输出: a\nb
  • 非贪婪匹配

    • 与重复符(如 *+)结合使用时,. 可以进行非贪婪匹配,尽可能少地匹配字符。

    示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    import re

    text = '<div>Content</div><div>More Content</div>'

    # 贪婪匹配
    pattern = r'<div>.*</div>'
    match = re.search(pattern, text)
    print(match.group()) # 输出: <div>Content</div><div>More Content</div>

    # 非贪婪匹配
    pattern = r'<div>.*?</div>'
    match = re.search(pattern, text)
    print(match.group()) # 输出: <div>Content</div>

常见误区

  • 误认为 . 匹配换行符
    • 很多人初学时误以为 . 可以匹配所有字符,包括换行符。实际上,需要使用单行模式或特定的标志来实现这一点。
  • 过度依赖 . 进行匹配
    • 虽然 . 是强大的通配符,但在某些情况下,过度使用可能导致匹配过于宽泛,难以控制。应根据具体需求合理使用。

最佳实践

  • 明确需求:在使用 . 时,确保明确知道它匹配的范围,必要时使用限定符或其他元字符进行精确控制。

  • 结合其他元字符. 通常与其他元字符(如 *, +, ?, [], () 等)结合使用,以构建更复杂和精确的匹配模式。

  • 使用模式标志:根据需要调整正则表达式的行为,如使用 re.DOTALL. 匹配换行符,或使用 re.IGNORECASE 进行不区分大小写的匹配。

更多示例

  1. 匹配电子邮件地址中的任意字符

    1
    2
    3
    4
    5
    6
    import re

    pattern = r'\w+@\w+\.\w+'
    text = 'Contact us at support@example.com'
    match = re.search(pattern, text)
    print(match.group()) # 输出: support@example.com
  2. 匹配包含特殊字符的文件名

    1
    2
    3
    4
    5
    6
    import re

    pattern = r'file\.\w+'
    text = 'The file.txt is ready.'
    match = re.search(pattern, text)
    print(match.group()) # 输出: file.txt
  3. 使用 . 进行复杂匹配

    1
    2
    3
    4
    5
    6
    7
    import re

    # 匹配以 "start" 开头,以 "end" 结尾,中间有任意字符
    pattern = r'^start.*end$'
    text = 'start123end'
    match = re.match(pattern, text)
    print(match.group()) # 输出: start123end

三、匹配一组字符

  1. 字符集
    • 定义:字符集使用方括号 [] 包含一组字符,表示可以匹配方括号内的任意一个字符。
    • 匹配逻辑:正则表达式引擎会尝试匹配字符串中的单个字符,只要这个字符出现在字符集中即可。
    • 示例
      • 正则表达式 [abc]
        • 匹配字符串中的 "a"、"b" 或 "c" 中的任意一个字符。
        • 如输入 "apple":[abc] 会匹配 "a"。
        • 如输入 "bake":[abc] 会匹配 "b"。
        • 如输入 "cat":[abc] 会匹配 "c"。
    • 组合使用
      • [aeiou]:匹配任意一个元音字母("a"、"e"、"i"、"o"、"u")。
      • [0123456789]:匹配任意一个数字字符。
    • 字符集内的特殊符号
      • 如果字符集内包含元字符如 .*+ 等,它们在字符集中失去特殊意义,仅作为普通字符处理。如:
        • [.*+] 匹配的是 "."、"*"、"+" 三个字符中的任意一个。
  2. 范围
    • 定义:字符集内的字符可以使用连字符 - 来表示一个范围,这样可以简化书写。
    • 示例
      • [a-z]:匹配所有小写字母,从 "a" 到 "z"。
        • 如输入 "dog":正则 [a-z] 可以匹配 "d"、"o"、"g" 中的任何一个。
        • [a-zA-Z]:匹配所有字母,不区分大小写。
      • [0-9]:匹配所有数字,从 "0" 到 "9"。
        • 如输入 "2024":正则 [0-9] 可以匹配 "2"、"0"、"2"、"4" 中的任何一个。
    • 多个范围组合
      • [a-z0-9]:匹配小写字母和数字,即小写字母或数字中的任意一个。
      • [A-Fa-f0-9]:匹配十六进制数字中的任意一个字符,包括 "A-F"、"a-f"、"0-9"。
    • 范围的注意事项
      • 范围中的起始字符和结束字符必须按照 ASCII 码的顺序,否则可能产生错误的结果。
      • 例如,[z-a] 是无效的,因为 "z" 的 ASCII 码大于 "a"。
  3. 否定字符集
    • 定义:使用 ^ 在字符集的开头表示否定字符集,即匹配不在字符集内的任何字符。
    • 示例
      • [^abc]:匹配除了 "a"、"b"、"c" 之外的任意字符。
        • 如输入 "dog":[^abc] 匹配 "d"、"o"、"g"。
      • [^0-9]:匹配非数字的字符。
        • 如输入 "Hello123":[^0-9] 匹配 "H"、"e"、"l"、"l"、"o"。
  • 字符集 [ ] 是匹配任意一个包含的字符。
  • 范围 [a-z] 可以表示连续字符的范围,简化字符集定义。
  • 否定字符集 [^ ] 用于匹配不在指定字符集中的字符。

四、使用元字符

元字符是具有特殊意义的字符,能够帮助我们构建更强大的正则表达式。以下是常见的元字符及其功能:

  1. \d(匹配数字):
    • 定义\d 匹配任意的数字字符。这个元字符等价于字符集 [0-9],也就是说它可以匹配任意一个从 "0" 到 "9" 的数字。
    • 示例
      • 正则表达式 \d 可以匹配字符串中的任何一个数字。
      • 如输入 "Price: 45 dollars":
        • \d 匹配 "4" 和 "5"。
        • \d\d 可以匹配 "45"(连续的两个数字)。
      • 正则 \d{3} 匹配三位数字的组合,如 "123" 或 "456"。
  2. \w(匹配字母、数字或下划线):
    • 定义\w 匹配任何字母(大写或小写)、数字或下划线 _。它等价于字符集 [A-Za-z0-9_]
    • 示例
      • 正则表达式 \w 可以匹配任何一个字母、数字或下划线。
      • 如输入 "user_name123":
        • \w 匹配 "u"、"s"、"e"、"r"、"_"、"n"、"a"、"m"、"e"、"1"、"2"、"3"。
        • \w+ 可以匹配整个字符串 "user_name123"。
      • 正则 \w{5} 可以匹配连续五个字母、数字或下划线的组合,如 "abc12" 或 "user_1"。
  3. \s(匹配空白字符):
    • 定义\s 匹配任何空白字符,包括空格、制表符(Tab)、换行符(\n)和回车符(\r)。这对于处理带有空格或其他空白字符的文本时非常有用。
    • 示例
      • 正则表达式 \s 可以匹配字符串中的任意一个空白字符。
      • 如输入 "Hello World":
        • \s 匹配两个单词之间的空格。
      • 如输入 "Line1":
        • \s 可以匹配换行符。
      • 正则 \s+ 可以匹配一个或多个连续的空白字符,适合用来处理多个空格的情况。
  4. \D(匹配非数字):
    • 定义\D\d 的反义,匹配任何非数字字符。
    • 示例
      • 正则表达式 \D 可以匹配字符串中除了数字以外的任意字符。
      • 如输入 "Room 101":
        • \D 匹配 "R"、"o"、"o"、"m"、" "(空格)。
      • \D+ 匹配连续的非数字字符,如 "Room "。
  5. \W(匹配非字母、数字或下划线):
    • 定义\W\w 的反义,匹配任何非字母、非数字或非下划线的字符。
    • 示例
      • 正则表达式 \W 可以匹配除了字母、数字和下划线外的任意字符,如标点符号、空格等。
      • 如输入 "Hello, World!":
        • \W 匹配逗号 ","、空格 " " 和感叹号 "!"。
      • \W+ 匹配一个或多个连续的非字母、非数字或非下划线的字符,如匹配 ", World!"。
  6. \S(匹配非空白字符):
    • 定义\S\s 的反义,匹配任何非空白字符。
    • 示例
      • 正则表达式 \S 可以匹配除了空格、制表符、换行符外的任意字符。
      • 如输入 "Hello World":
        • \S 匹配 "H"、"e"、"l"、"l"、"o"、"W"、"o"、"r"、"l"、"d"。
        • \S+ 可以匹配 "Hello" 和 "World" 两个单词。
  • \d:匹配任何数字字符,等价于 [0-9]
  • \w:匹配字母、数字或下划线,等价于 [A-Za-z0-9_]
  • \s:匹配任何空白字符,包括空格、制表符、换行符等。
  • \D\W\S:分别匹配非数字、非字母/数字/下划线、非空白字符。

五、重复匹配

重复匹配操作符用于指定前一个字符或子表达式出现的次数,从而构建灵活的正则表达式。这些操作符能够匹配多次重复的字符序列。以下是常见的重复匹配操作符及其使用方法:

  1. *(匹配前一个字符0次或多次)
    • 定义* 表示匹配前一个字符0次或多次,即前一个字符可以不出现,也可以出现多次。
    • 示例
      • 正则表达式 a*
        • 匹配空字符串(因为 "a" 可以出现0次)以及任何包含多个 "a" 的字符串。
        • 如输入 "":a* 匹配空字符串。
        • 如输入 "aa":a* 匹配整个字符串 "aa"。
        • 如输入 "baaa":a* 匹配 "aaa" 部分。
      • .*(点号 . 匹配任意字符,* 匹配0次或多次):
        • 匹配任意长度的字符串,包括空字符串。
        • 如输入 "hello world":.* 匹配整个字符串。
  2. +(匹配前一个字符1次或多次)
    • 定义+ 表示匹配前一个字符1次或多次,即前一个字符至少出现一次。
    • 示例
      • 正则表达式 a+
        • 匹配一个或多个连续的 "a" 字符。
        • 如输入 "a":a+ 匹配 "a"。
        • 如输入 "aaa":a+ 匹配 "aaa"。
        • 如输入 "baaa":a+ 匹配 "aaa" 部分。
      • \d+\d 匹配数字,+ 匹配1次或多次):
        • 匹配一个或多个连续的数字。
        • 如输入 "123abc456":\d+ 分别匹配 "123" 和 "456"。
  3. ?(匹配前一个字符0次或1次)
    • 定义? 表示匹配前一个字符0次或1次,即前一个字符可以出现一次或不出现。
    • 示例
      • 正则表达式 a?
        • 匹配0个或1个 "a" 字符。
        • 如输入 "":a? 匹配空字符串。
        • 如输入 "a":a? 匹配 "a"。
        • 如输入 "ab":a? 匹配 "a" 部分。
      • 正则表达式 colou?r
        • u? 匹配 "u" 0次或1次,能够匹配 "color" 或 "colour"。
        • 如输入 "color":colou?r 匹配整个字符串。
        • 如输入 "colour":colou?r 也匹配整个字符串。
  4. {n}(匹配前一个字符恰好n次)
    • 定义{n} 表示匹配前一个字符恰好 n 次,即前一个字符必须精确地出现 n 次。
    • 示例
      • 正则表达式 a{3}
        • 匹配连续出现的三个 "a" 字符。
        • 如输入 "aaa":a{3} 匹配整个字符串 "aaa"。
        • 如输入 "aaaaa":a{3} 匹配前三个 "a"。
      • 正则表达式 \d{4}
        • 匹配恰好四位的数字。
        • 如输入 "2024年":\d{4} 匹配 "2024"。
  5. {n,m}(匹配前一个字符n到m次)
    • 定义{n,m} 表示匹配前一个字符至少 n 次,至多 m 次。n 和 m 必须是非负整数。
    • 示例
      • 正则表达式 a{2,4}
        • 匹配两个到四个连续的 "a" 字符。
        • 如输入 "aa":a{2,4} 匹配 "aa"。
        • 如输入 "aaa":a{2,4} 匹配 "aaa"。
        • 如输入 "aaaa":a{2,4} 匹配 "aaaa"。
        • 如输入 "aaaaa":a{2,4} 匹配前四个 "a"。
      • 正则表达式 \d{2,5}
        • 匹配至少两位、至多五位的数字。
        • 如输入 "12345":\d{2,5} 匹配 "12345"。
        • 如输入 "123456":\d{2,5} 匹配前五个数字 "12345"。
  • *:匹配前一个字符0次或多次,适合匹配任意数量的字符,包括空字符。
  • +:匹配前一个字符1次或多次,要求至少匹配一个字符。
  • ?:匹配前一个字符0次或1次,通常用于匹配可选字符。
  • {n}:匹配前一个字符恰好 n 次,适用于精确匹配字符出现的次数。
  • {n,m}:匹配前一个字符至少 n 次,至多 m 次,提供了灵活的次数范围匹配。

六、位置匹配

位置匹配是一种特殊的匹配方式,它并不匹配具体的字符,而是匹配字符在字符串中的位置。这些位置匹配符号可以用于构建对字符串特定部分的精确匹配条件。以下是常用的几种位置匹配符号及其详细说明:

  1. ^(匹配字符串的起始位置)
    • 定义^ 表示匹配字符串的起始位置,通常用于确保字符串以特定字符或子字符串开头。
    • 示例
      • 正则表达式 ^a
        • 匹配任何以 "a" 开头的字符串。
        • 如输入 "apple":^a 匹配 "a"(因为 "apple" 以 "a" 开头)。
        • 如输入 "banana":^a 不匹配任何内容(因为 "banana" 不是以 "a" 开头)。
      • 正则表达式 ^Hello
        • 匹配以 "Hello" 开头的字符串。
        • 如输入 "Hello, world!":^Hello 匹配 "Hello"。
        • 如输入 "Hi, Hello":^Hello 不匹配任何内容。
  2. $(匹配字符串的结束位置)
    • 定义$ 表示匹配字符串的结束位置,通常用于确保字符串以特定字符或子字符串结尾。
    • 示例
      • 正则表达式 a$
        • 匹配任何以 "a" 结尾的字符串。
        • 如输入 "banana":a$ 匹配最后的 "a"。
        • 如输入 "bananas":a$ 不匹配任何内容(因为 "bananas" 不是以 "a" 结尾)。
      • 正则表达式 \d{4}$
        • 匹配以四位数字结尾的字符串。
        • 如输入 "Year2024":\d{4}$ 匹配 "2024"。
        • 如输入 "ID20245":\d{4}$ 不匹配任何内容(因为 "ID20245" 的最后四位不是单独的数字)。
  3. \b(匹配单词边界)
    • 定义\b 表示单词的边界,即字符的前后位置中,一个是字母数字字符([A-Za-z0-9_]),另一个不是。它常用于确保匹配的内容是完整的单词,而不是单词的一部分。
    • 示例
      • 正则表达式 \bword\b
        • 匹配完整的单词 "word"。
        • 如输入 "word":\bword\b 匹配整个字符串 "word"。
        • 如输入 "wordplay":\bword\b 不匹配(因为 "word" 是 "wordplay" 的一部分)。
        • 如输入 "word, play":\bword\b 匹配 "word"(因为逗号是非字母字符)。
      • 正则表达式 \bcat\b
        • 匹配单独的单词 "cat"。
        • 如输入 "a black cat":\bcat\b 匹配 "cat"。
        • 如输入 "caterpillar":\bcat\b 不匹配(因为 "cat" 是 "caterpillar" 的一部分)。
  4. \B(匹配非单词边界)
    • 定义\B 表示非单词边界,即字符的前后位置中,两个都是字母数字字符或两个都不是。它用于确保匹配的内容在单词内部,或在非单词字符的中间。
    • 示例
      • 正则表达式 \Bcat\B
        • 匹配单词内部的 "cat"。
        • 如输入 "scattering":\Bcat\B 匹配 "cat" 部分。
        • 如输入 "cat":\Bcat\B 不匹配(因为 "cat" 是完整的单词,没有被其他字符包围)。
      • 正则表达式 \Bing
        • 匹配任何在单词内部的 "ing"。
        • 如输入 "singing":\Bing 匹配 "ing" 部分(在单词内部)。
        • 如输入 "ring":\Bing 不匹配(因为 "ing" 是单词结尾部分)。
  • ^:用于匹配字符串的开头位置,确保匹配在字符串的最前面。
  • $:用于匹配字符串的结尾位置,确保匹配在字符串的最后面。
  • \b:用于匹配单词的边界,确保匹配的内容是独立的单词或部分。
  • \B:用于匹配非单词边界,确保匹配的内容在单词内部或非单词字符的中间。

七、使用子表达式

子表达式是正则表达式中一个非常强大的工具,通过将表达式的某一部分括起来,可以对它们进行特殊的处理和操作。这使得正则表达式更加灵活和强大,能够实现复杂的匹配需求。

1. 括号 ():定义子表达式

  • 定义:括号 () 用于将正则表达式的一部分括起来,使其成为一个整体,这部分表达式就称为一个子表达式。
  • 作用
    • 分组:将多个字符或表达式作为一个整体对待,以便应用量词、位置匹配或其他操作。
    • 捕获:默认情况下,子表达式会捕获匹配的内容,并将其存储在一个临时的缓存区中,可以在后续操作中引用这些匹配的内容。
    • 重用:可以在表达式中后续引用之前的捕获组。
  • 示例
    • 正则表达式 (abc)+
      • 匹配字符串 "abc" 的一次或多次重复出现。
      • 如输入 "abcabc":(abc)+ 匹配整个字符串 "abcabc"。
      • 如输入 "abcabcabc":(abc)+ 匹配整个字符串 "abcabcabc"。
    • 正则表达式 (ab|cd)+ef
      • 匹配 "ab" 或 "cd" 这两个子串的一次或多次重复出现,并且最后匹配 "ef"。
      • 如输入 "abef":匹配 "abef"。
      • 如输入 "cdabef":匹配 "cdabef"。
      • 如输入 "abcdcdef":匹配 "abcdcdef"。

2. 非捕获组 (?:...):定义子表达式但不捕获结果

  • 定义:非捕获组 (?:...) 也是一种括号语法,用于将表达式的一部分括起来,但与普通括号不同,它不会捕获匹配的内容,不会影响后续捕获组的编号。这种分组在不需要保存匹配结果时非常有用。
  • 作用
    • 优化性能:由于不保存匹配结果,因此非捕获组的性能可能会比捕获组略好。
    • 避免编号冲突:在复杂的正则表达式中,使用非捕获组可以避免不必要的捕获,保持捕获组的编号一致性。
  • 示例
    • 正则表达式 (?:abc)+
      • 匹配字符串 "abc" 的一次或多次重复出现,但不保存匹配结果。
      • 如输入 "abcabc":(?:abc)+ 匹配整个字符串 "abcabc",但不会将 "abc" 保存为一个捕获组。
    • 正则表达式 (?:ab|cd)+ef
      • 匹配 "ab" 或 "cd" 这两个子串的一次或多次重复出现,并且最后匹配 "ef",但不会捕获 "ab" 或 "cd"。
      • 如输入 "abef":匹配 "abef"。
      • 如输入 "cdabef":匹配 "cdabef"。

子表达式的捕获和引用

  1. 捕获组的引用:在正则表达式内部,可以通过 \1, \2, \3 等方式来引用之前捕获的组。这在进行复杂匹配或替换时非常有用。
    • 示例
      • 正则表达式 (\d{3})-(\d{2})-(\d{4})
        • 匹配形如 "123-45-6789" 的数字格式,并捕获每一部分。
        • \1 引用第一个捕获组,即前三位数字 "123"。
        • \2 引用第二个捕获组,即中间两位数字 "45"。
        • \3 引用第三个捕获组,即最后四位数字 "6789"。
  2. 替换中的捕获组:在字符串替换操作中,可以使用捕获组来调整或重新排列匹配的内容。
    • 示例
      • 使用正则表达式 (\w+)\s(\w+) 替换 "first last""last, first"
        • 捕获第一个单词 (\w+),然后捕获第二个单词 (\w+)
        • 替换字符串可以是 "\2, \1",将名字和姓氏对调。
  • ():定义捕获组,允许分组和捕获匹配的子表达式内容,用于更复杂的匹配和替换。
  • (?:...):定义非捕获组,用于分组但不捕获匹配结果,适用于不需要保存匹配内容的场景。
  • 捕获组的引用:可以通过 \1, \2 等在表达式内部引用已捕获的内容,或在替换操作中使用捕获组来调整文本。

八、回溯引用

回溯引用(backreference)是一种强大的正则表达式功能,允许在同一个表达式中引用之前捕获的子表达式的匹配内容。这在处理重复的结构或需要保持一致性的匹配时非常有用。

1. 回溯引用的基本概念

  • 定义:回溯引用是指在正则表达式中,通过特定的符号 \1, \2, \3 等,引用在表达式中之前定义和捕获的子表达式。
  • 用途:回溯引用常用于匹配重复出现的内容,或在需要对相同模式进行一致性检查时使用。

2. 使用方法

  • 基本语法(\text{pattern})\1 表示匹配一个模式并再次匹配相同的模式。
    • (\d):表示一个数字,并将其捕获为第一个子表达式。
    • \1:引用第一个子表达式,要求匹配的字符与之前捕获的内容一致。
  • 多个回溯引用:如果有多个子表达式捕获组,可以使用 \2, \3 等引用它们。
    • (\d)(\w)\2\1
      • (\d) 捕获一个数字并存储为 \1
      • (\w) 捕获一个字母并存储为 \2
      • \2\1 要求匹配捕获的字母和数字的重复顺序。

3. 回溯引用的实际应用

  • 匹配重复字符
    • 示例:正则表达式 (\d)\1 匹配两个连续的相同数字。
      • 如输入 "00":匹配 "00"。
      • 如输入 "112233":可以匹配 "11", "22", "33"。
      • 解释(\d) 捕获一个数字并存储为第一个子表达式,\1 要求第二个匹配的字符必须与第一个相同。
  • 匹配重复的字母组合
    • 示例:正则表达式 (\w\w)\1 匹配两个相同的字母组合。
      • 如输入 "abab":匹配 "abab"。
      • 如输入 "xyxy":匹配 "xyxy"。
      • 解释(\w\w) 捕获两个字母并存储为第一个子表达式,\1 要求匹配的下两个字符与之前捕获的内容一致。
  • 匹配回文
    • 示例:正则表达式 (\w)(\w)\2\1 匹配一个四个字母的回文结构,如 "abba"。
      • 如输入 "abba":匹配 "abba"。
      • 如输入 "xyyx":匹配 "xyyx"。
      • 解释(\w) 捕获第一个和第二个字母分别为 \1\2,接着 \2\1 匹配前两个字母的逆序。

4. 特殊场景中的回溯引用

  • 替换中的回溯引用:在替换操作中,回溯引用可以用来重新排列或调整匹配内容。
    • 示例:使用正则表达式 (\w+)\s(\w+) 替换 "first last""last, first"
      • 捕获第一个单词 (\w+),并将其存储为 \1
      • 捕获第二个单词 (\w+),并将其存储为 \2
      • 替换表达式使用 "\2, \1",将名字和姓氏对调。
  • 嵌套回溯引用:在复杂表达式中,回溯引用可以嵌套使用,匹配更多层次的内容。
    • 示例:正则表达式 ((\d)\d)\2 匹配特定的嵌套结构。
      • 解释(\d) 捕获一个数字,(\d)\d 捕获两个数字并作为一个整体引用,\2 匹配第一个数字。
  • 回溯引用 \1, \2 等允许在正则表达式中引用之前捕获的内容。
  • 回溯引用可以用来匹配重复的字符、字母组合,或检查相同模式的一致性。
  • 回溯引用在替换操作中也非常有用,可以重新排列或调整匹配的内容。
  • 在复杂表达式中,回溯引用可以嵌套使用,实现更加高级的匹配需求。

九、前后查找

前后查找(lookahead 和 lookbehind)是一种高级正则表达式技术,用于在不消耗字符的情况下,检查某个匹配是否满足特定条件。这意味着前后查找本身并不会返回匹配的字符,而是作为一种限制条件来约束其他匹配。

1. 前瞻(Lookahead)

前瞻用于判断一个字符串是否在某个特定的内容之前出现,但这个内容不会包含在最终的匹配结果中。

  • 语法(?=...)

  • 解释(?=...) 中的 ... 是一个子表达式,表示如果当前位置满足 ... 的条件,那么匹配成功,但实际的匹配结果不包括 ...

  • 示例

    • 正则表达式:\d(?=px)
      • 作用:匹配紧跟在 "px" 前的数字。
      • 匹配字符串:"20px"
      • 匹配结果20 中的 2,因为它后面跟着 "px"。
  • 应用场景

    • 例如在样式表中,你可能想匹配所有以 "px" 为单位的数字:
      • 输入:"margin: 10px; padding: 5px; font-size: 12pt;"
      • 匹配正则:\d+(?=px)
      • 结果:105,因为它们后面都跟着 "px"。

2. 后顾(Lookbehind)

后顾用于判断一个字符串是否在某个特定的内容之后出现,并且这个内容不会包含在最终的匹配结果中。

  • 语法(?<=...)

  • 解释(?<=...) 中的 ... 是一个子表达式,表示如果当前位置之前的字符串满足 ... 的条件,那么匹配成功,但实际的匹配结果不包括 ...

  • 示例

    • 正则表达式:(?<=\$)\d+
      • 作用:匹配紧跟在美元符号 $ 后的数字。
      • 匹配字符串:"总计: $100"
      • 匹配结果100,因为它前面有 $ 符号。
  • 应用场景

    • 例如在财务数据中,你可能想提取所有以美元表示的数值:
      • 输入:"价格:$50, $100, ¥200"
      • 匹配正则:(?<=\$)\d+
      • 结果:50100,因为它们前面都跟着 "$" 符号。

3. 负前瞻(Negative Lookahead)

负前瞻用于判断某个字符串是否不在某个特定内容之前出现,且这个内容不会包含在最终的匹配结果中。

  • 语法(?!...)

  • 解释(?!...) 中的 ... 是一个子表达式,表示如果当前位置满足 ... 的条件,则匹配失败,只有不满足 ... 的情况下,匹配才成功。

  • 示例

    • 正则表达式:\d(?!px)
      • 作用:匹配后面不跟 "px" 的数字。
      • 匹配字符串:"20pt 30px"
      • 匹配结果20,因为它们后面不跟 "px"。
  • 应用场景

    • 例如在文本中,过滤掉特定单位的数值,如排除以 "px" 为单位的数字:
      • 输入:"margin: 20px; padding: 10pt; font-size: 15px;"
      • 匹配正则:\d+(?!px)
      • 结果:10,因为它后面没有 "px"。

4. 负后顾(Negative Lookbehind)

负后顾用于判断某个字符串是否不在某个特定内容之后出现,且这个内容不会包含在最终的匹配结果中。

  • 语法(?<!...)

  • 解释(?<!...) 中的 ... 是一个子表达式,表示如果当前位置之前的字符串满足 ... 的条件,则匹配失败,只有不满足 ... 的情况下,匹配才成功。

  • 示例

    • 正则表达式:(?<!\$)\d+
      • 作用:匹配前面不跟 $ 的数字。
      • 匹配字符串:"总计: 100 元, $200 美元"
      • 匹配结果100,因为它前面没有 $ 符号。
  • 应用场景

    • 例如在文本中,排除特定货币符号的数值:
      • 输入:"$100, 200元, $300"
      • 匹配正则:(?<!\$)\d+
      • 结果:200,因为它前面没有 "$"。
  • 前瞻 (?=...)后顾 (?<=...) 用于在不包含条件本身的情况下,对字符串进行条件约束匹配。
  • 负前瞻 (?!...)负后顾 (?<!...) 则用于排除特定条件,使得匹配更加灵活。
  • 前后查找的强大之处在于它们不消耗字符,因此能够与其他正则表达式结合使用,实现复杂的匹配需求。

十、嵌入条件

正则表达式中的嵌入条件(conditional expressions)允许根据前面的匹配结果选择不同的匹配路径。这使得正则表达式能够处理更复杂的匹配场景,类似于编程中的条件语句。

1. 条件表达式的语法

  • 语法(?ifthen|else)
    • if:表示一个条件子表达式。如果这个子表达式成功匹配,选择 then 部分。
    • then:如果 if 部分成功,匹配这个部分。
    • else:如果 if 部分失败,则匹配这个部分(else 是可选的)。

2. 使用条件表达式的示例

  • 基本示例
    • 正则表达式(?:(\d)\d{2}|(\w)\w{2})
      • 解释:这里的正则表达式表示,如果第一个捕获组(\d)成功匹配,后面必须是两个数字(\d{2})。否则,匹配第二个捕获组(\w)后面跟两个字母(\w{2})。
      • 输入:"12ab"
        • 匹配结果12 匹配第一个部分;ab 匹配第二个部分。
  • 复杂示例
    • 正则表达式(?:(\d{3})|(\w{2}))(?(1)\d{2}|\w{2})
      • 解释:如果第一个捕获组((\d{3}))成功匹配三个数字,那么匹配后面两个数字(\d{2})。如果第一个捕获组没有匹配(即匹配两个字母 (\w{2})),则匹配后面两个字母(\w{2})。
      • 输入:"12345"
        • 匹配结果123 匹配第一个部分;45 匹配第二个部分。
      • 输入:"abxy"
        • 匹配结果ab 匹配第一个部分;xy 匹配第二个部分。

3. 应用场景

  • 验证特定格式的输入
    • 在需要验证用户输入的格式时,条件表达式可以根据特定条件选择不同的验证规则。例如,验证电话号码或身份证号码的格式,可能根据国家或区域不同而有所变化。
  • 处理可变格式的数据
    • 当数据格式可变时,例如日志文件中可能包含不同类型的记录,条件表达式可以根据记录类型选择不同的解析方式。
  • 复杂文本处理
    • 在文本处理任务中,特别是那些涉及到多种格式的文本,条件表达式可以根据先前的匹配结果选择适当的后续匹配路径,从而进行更复杂的文本提取。

4. 正则表达式的实际例子

  • 例子 1:匹配一个可能是数字或字母,并且其后的部分是固定格式。
    • 正则表达式(?:(\d)\d{2}|(\w)\w{2})(?(1)\d{2}|\w{2})
      • 解释:如果第一个捕获组匹配数字,那么后面必须是两个数字;如果第一个捕获组匹配字母,那么后面必须是两个字母。
      • 输入:"12345" 匹配成功,12345 分别对应数字和两个数字。
      • 输入:"abxy" 匹配成功,abxy 分别对应字母和两个字母。
  • 例子 2:匹配一个字符串,前缀是数字或字母,并且根据前缀的不同,后缀的规则也不同。
    • 正则表达式(?:(\d{2,3})|(\w{1,2}))(?(1)\d{3,4}|\w{3,4})
      • 解释:如果前面匹配了两个或三个数字,则后面匹配三个或四个数字;如果前面匹配了一个或两个字母,则后面匹配三个或四个字母。
      • 输入:"1234567" 匹配成功,1234567 分别对应两个数字和三个数字。
      • 输入:"abxyz" 匹配成功,abxyz 分别对应字母和三个字母。
  • 条件表达式 (?ifthen|else) 使得正则表达式能够根据条件选择不同的匹配路径。
  • 使用场景:验证格式、处理可变格式的数据、复杂文本处理等。
  • 实例:可以处理不同格式的数据,根据先前的匹配结果决定后续匹配的内容。