目录

  • 问题
  • 解决方案
  • 讨论

问题

我们打算使用正则表达式对一段文本做匹配,但是希望在进行匹配时能够跨越多行。换句话说,正则表达式中 . 可以匹配任意除了换行符的字符,我们如何让他也能够匹配换行符?

解决方案

例如在如下的两个案例中,text1 为单行内容,text2 内容分在了多行,pattern 为正则化匹配模式。

import re

pattern = re.compile(r'/\*(.*?)\*/')
text1 = "/* This is a commment */"
text2 = '''/* This is a
multiline comment.*/'''

print("text1 result:", pattern.findall(text1))
print("text2 result:", pattern.findall(text2))

结果:

text1 result: [' This is a commment ']
text2 result: []

关于正则化的匹配模式,在这里简述,若读者想要了解更清楚,可以移步博文:【Python Cookbook】S01E24 如何定义正则表达式模式从而准确匹配内容,通过 ? 调整贪心策略为非贪婪

在正则化匹配模式 '/\*(.*?)\*/'

  • /\*:这里有三个字符,分别是 / \ *,其目标是为了匹配字符串中的 /* 内容。而另外的一个字符,反斜杠 \,则是用于转义星号,使其成为普通字符。

  • (.*?):这是一个捕获组。

    • 最基础的圆括号 () 表示捕获匹配的内容
    • . 表示任意字符(除了换行符)
    • ? 表示非贪婪匹配模式,即尽可能少地匹配字符
    • * 表示前一个字符可以出现 0 次或多次。因此,这个捕获组会匹配尽可能少的任意字符,当然除了换行符。
  • \*/:与前面的 /* 类似,匹配字符串中的 */ 符号。同样,反斜杠用于转义星号,使其成为普通字符。

总而言之,上述正则表达式模式用于匹配被 /**/ 包裹的任意文本。但是,不包含换行符。

所以我们应该如何实现换行符也能够匹配呢?通过加入

pattern = re.compile(r'/\*(.*?|.*?
.*?)\*/')

优化上述模式匹配为:

pattern = re.compile(r'/\*((.|
)*?)\*/')

正则化匹配模式 /\*((.|
)*?)\*/
解释:

前后的内容 /\* \*/ 上述已讲述,不过多赘述,
读者应当也熟识,为标准的换行符。

  • ((.|
    )*?)
    模式中,各部分含义为:

    • . 还是表示任意除了换行符的字符
    • | 或者的含义,没有加转义字符就是原意
    • (.|
      )
      的含义即,“不包含换行符” 或 “换行符”,即,包含换行符的任意字符
    • * 表示前一个字符可以出现 0 次或多次,在这里,(.|
      )*
      即可以出现多次任意字符
    • ? 非贪婪模式,结合到整体模式中,即不贪婪的匹配所有在 /* */ 中的,包含换行符的任意字符。

再优化:

pattern = re.compile(r'/\*((?:.|
)*?)\*/')

新增内容为 ?:,这是什么含义?

  • 在正则化匹配中,?: 是一个非捕获组的标记。其创建一个分组,但不同于普通捕获组,非捕获组不会保存匹配到的文本,也不会影响正则表达式引擎的回溯引用功能。

  • 区别于普通捕获组,其可以避免额外的存储开销,因为它告诉正则表达式引擎不要保存该分组的匹配结果。

讨论

上文我们花了大片篇幅来说明如何使得匹配时候包含换行符,但是其实,re.compile() 函数可接受一个有用的标记,re.DOTALL,这个标识使得正则表达式中的句点 . 可以匹配包含换行符在内的所有符号内容。

import re

pattern = re.compile(r'/\*(.*?)\*/', re.DOTALL)
text1 = "/* This is a commment */"
text2 = '''/* This is a
multiline comment.*/'''

print("text1 result:", pattern.findall(text1))
print("text2 result:", pattern.findall(text2))

结果:

text1 result: [' This is a commment ']
text2 result: [' This is a
multiline comment.']

综上所述,对于简单的情况,我们使用 re.DOTALL 标记就可以很好的完成工作。但是如果要处理及其复杂的情况,就需要我们自行定义正则化表达式模式了。

本站无任何商业行为
个人在线分享 » 【Python Cookbook】S02E08 编写多行模式的正则表达式
E-->