首页 > 解决方案 > (Python)如何根据几个正则表达式检查一个长字符串?

问题描述

我想确保一个长字符串可以一次匹配多个正则表达式。

我有一个长的多行字符串,其中包含文件列表和文件的一些内容。

DIR1\FILE1.EXT1 CONTENT11
DIR1\FILE1.EXT1 CONTENT12
DIR1\FILE1.EXT1 CONTENT13
DIR1\FILE2.EXT1 CONTENT21
DIR2\FILE3.EXT2 CONTENT31
DIR3\FILE3.EXT2 CONTENT11

该列表通常包含数十万行,有时甚至数百万行。

我想检查列表是否包含预定义的情侣文件/内容:

FILE1 CONTENT11
FILE1 CONTENT12
FILE3 CONTENT11

我知道我可以通过将字符串与一些正则表达式进行匹配来检查字符串是否包含所有这些对

"^\S*FILE1\S*\tCONTENT11$"
"^\S*FILE1\S*\tCONTENT12$"
"^\S*FILE3\S*\tCONTENT11$"
import re

def all_matching(str, rxs):
    res = True
    for rx in rxs:
        p = re.compile(rx, re.M)
        res = res and p.search(str)
    return(res)

input1 = """DIR1\\FILE1.EXT1\tCONTENT11
DIR1\\FILE1.EXT1\tCONTENT12
DIR1\\FILE1.EXT1\tCONTENT13
DIR1\\FILE2.EXT1\tCONTENT21
DIR2\\FILE3.EXT2\tCONTENT31
DIR3\\FILE3.EXT2\tCONTENT11"""

input2 = """DIR1\\FILE1.EXT1\tCONTENT11
DIR1\\FILE1.EXT1\tCONTENT12
DIR1\\FILE1.EXT1\tCONTENT13
DIR1\\FILE2.EXT1\tCONTENT21
DIR2\\FILE3.EXT2\tCONTENT31"""

rxs = [r"^\S*FILE1\S*\tCONTENT11$",r"^\S*FILE1\S*\tCONTENT12$",r"^\S*FILE3\S*\tCONTENT11$"]

if all_matching(input1,rxs):
    print("input1 matches all rxs") # excpected
else:
    print("input1 do not match all rxs")

if all_matching(input2,rxs):
    print("input2 matches all rxs")
else:
    print("input2 do not match all rxs") # expected because input2 doesn't match wirh rxs[2]

ideone 在这里可用

但是,由于在我的情况下输入字符串很长,我宁愿避免多次启动搜索......
我觉得应该可以以这种方式更改 all_matching 函数。

任何帮助都感激不尽!

编辑

通过提供的示例代码阐明了问题

标签: pythonregex

解决方案


您可以从您拥有的正则表达式字符串构建单个正则表达式,这将要求所有正则表达式在输入字符串中找到匹配项。

生成的正则表达式看起来像

\A(?=(?:.*\n)*?\S*FILE1\S*\tCONTENT11$)(?=(?:.*\n)*?\S*FILE1\S*\tCONTENT12$)(?=(?:.*\n)*?\S*FILE3\S*\tCONTENT11$)

请参阅正则表达式演示

基本上,它将匹配:

  • (?m)- 一个re.M/re.MULTILINE嵌入标志选项
  • \A- 字符串开头(不是行首!),下面所有的lookaheads都会被一个一个触发,从头开始检查字符串,直到其中一个失败
  • (?=(?:.*\n)*?\S*FILE1\S*\tCONTENT11$)- 一个积极的前瞻,即在当前位置的右侧,需要存在
    • (?:.*\n)*?- 0 或更多(但尽可能少,只有在后续子模式不匹配时才会尝试该模式)
    • \S*- 0+ 个非空格
    • FILE1- 一个字符串
    • \S*- 0+ 个非空格
    • \tCONTENT11- 制表符和CONTENT11子字符串
    • $- 行尾(因为(?m)允许$匹配行尾)
  • (?=(?:.*\n)*?\S*FILE1\S*\tCONTENT12$)- 与前一个类似的前瞻工作,需要FILE1CONTENT12子字符串在线
  • (?=(?:.*\n)*?\S*FILE3\S*\tCONTENT11$)- 与前一个类似的前瞻工作,需要FILE3CONTENT11子字符串。

在 Python 中,它看起来像

rxs = [r"^\S*FILE1\S*\tCONTENT11$",r"^\S*FILE1\S*\tCONTENT12$",r"^\S*FILE3\S*\tCONTENT11$"]
pat = re.compile( r"(?m)\A(?=(?:.*\n)*?{})".format(r")(?=(?:.*\n)*?".join([rx[1:] for rx in rxs])) )

然后,检查方法将如下所示

def all_matching(s, pat):
    return pat.search(s)

在线查看完整的 Python 演示


推荐阅读