【问题标题】:Regex for longest matching sequence between two strings两个字符串之间最长匹配序列的正则表达式
【发布时间】:2019-04-22 06:46:01
【问题描述】:

我在 Google 上搜索了我的用例,但没有发现任何有用的东西。

我不是正则表达式方面的专家,所以如果社区中的任何人可以提供帮助,我将不胜感激。

问题:

给定一个文本文件,我想使用正则表达式捕获两个子字符串(前缀和后缀)之间的最长字符串。请注意,这两个子字符串将始终位于文本任何行的开头。请看下面的例子。

子字符串:

前缀 = ['Item 1', 'Item 1a', 'Item 1b']
后缀 = ['项目 2', '项目 2a', '项目 2b']

示例 1:

第 1 项 ....
项目 2 ....
项目 1 ....
....
....
项目 2 ....
项目 1 ....
项目 2
项目 1a ....
....
....
....
....
项目 2b ....

预期结果:

第 1a 项 ....
....
....
....
....

为什么会出现这个结果?

因为Item 1a 的前缀和Item 2b 的后缀匹配所有其他前缀-后缀对中它们之间的文本中最长的字符串。

示例 2:

第 1 项 ....
项目 2 ....
项目 1 ....
....
....
项目 2
…… 项目 1 ....
项目 2
项目 1a .... ....
....
....
.... 项目 2b
....

预期结果:

第 1 项 ....
....
....

为什么会出现这个结果?

这是因为这是两个字符串(前缀和后缀对)之间的最大字符串,其中前缀和后缀都从行首开始。请注意,还有另一对 (Item 1a-Item 2b) 但由于Item 2b 不在行首,我们不能考虑这个最长的序列。

我对正则表达式的尝试:

我已经尝试为上面列表中的每个前缀-后缀对使用下面的正则表达式,但这不起作用。

regexs = [r'^' + re.escape(pre) + '(.*?)' + re.escape(suf) for pre in prefixes for suf in suffixes]
for regex in regexs:
    re.findall(regex, text, re.MULTLINE)

我尝试过使用非正则表达式(Python 字符串函数):

def extract_longest_match(text, prefixes, suffixes):
    longest_match = ''
    for line in text.splitlines():
        if line.startswith(tuple(prefixes)):
            beg_index = text.index(line)
            for suf in suffixes:
                end_index = text.find(suf, beg_index+len(line))
                match = text[beg_index:end_index]
                if len(match) > len(longest_match ):
                    longest_match = match
    return longest_match 

我错过了什么吗?

【问题讨论】:

  • 正则表达式无法匹配最长的子字符串。使用正则表达式或非正则表达式的解决方案来查找子字符串,并使用通用语言手段找到最长的。
  • 是的,这可以检查使用正则表达式返回的字符串的长度。但是你知道如何在前缀和后缀之间获取文本,前缀和后缀都从句子的左侧开始吗?我的正则表达式正确吗?
  • @WiktorStribiżew 正则表达式可以匹配最长的子字符串。问题在于(.*?) OP 明确使用非贪婪正则表达式。应该只是(.*) 而不是(.*?)。
  • @quant .* 不会产生最长的子字符串,它只是从最左边的前导分隔符匹配到最右边的尾随分隔符。这不一样。这是贪婪和最长/最短子串提取的常见混淆。
  • @WiktorStribiżew 那么我在这里错过了什么?什么是正确的正则表达式来匹配前缀和后缀之间的字符串。然后我可以稍后检查长度并在变量中维护最大的字符串。

标签: python regex string pattern-matching string-matching


【解决方案1】:

你需要

Python demo:

import re
s="""Item 1 ....
Item 2 ....
Item 1 ....
....
....
Item 2 ....
Item 1 ....
Item 2
Item 1a ....
....
....
....
....
Item 2b ...."""
prefixes = ['Item 1', 'Item 1a', 'Item 1b']
suffixes = ['Item 2', 'Item 2a', 'Item 2b']
rx = r"(?=^((?:{}).*?^(?:{})))".format("|".join(prefixes), "|".join(suffixes))
# Or, a version with word boundaries:
# rx = r"(?=^((?:{})\b.*?^(?:{})\b))".format("|".join(prefixes), "|".join(suffixes))
all_matches = re.findall(rx, s, re.S | re.M)
print(max(all_matches, key=len))

输出:

Item 1a ....
....
....
....
....
Item 2

正则表达式看起来像

(?sm)(?=^((?:Item 1|Item 1a|Item 1b).*?^(?:Item 2|Item 2a|Item 2b)))

有单词边界

(?sm)(?=^((?:Item 1|Item 1a|Item 1b)\b.*?^(?:Item 2|Item 2a|Item 2b)\b))

请参阅regex demo。

详情

  • (?sm) - re.S 和 re.M 标志
  • (?=^((?:Item 1|Item 1a|Item 1b).*?^(?:Item 2|Item 2a|Item 2b))) - 在任何位置匹配的正向前瞻,紧随其后的是一系列模式:
    • ^ - 行首
    • ((?:Item 1|Item 1a|Item 1b).*?^(?:Item 2|Item 2a|Item 2b)) - 第 1 组(此值与 re.findall 返回)
    • (?:Item 1|Item 1a|Item 1b) - 交替中的任何项目(在 ) 之后添加 \b 字边界可能是有意义的)
    • .*? - 任何 0+ 个字符,尽可能少
    • ^ - 行首
    • (?:Item 2|Item 2a|Item 2b) - 列表中的任何替代项(可能在 ) 之后添加 \b 字边界也是有意义的)。

【讨论】:

  • 感谢您的快速解决方案。我在测试示例(此处未显示)中看到的一个小问题是它还匹配字符串中任何位置的任何后缀。我想检查前缀和后缀都出现在行首的前缀-后缀对。抱歉,如果我在问题中的示例误导您不相信。 :-)
  • 我已经用示例 2 编辑了我的问题。所以示例 2 可能有助于更好地解释我的用例 :-)
  • 太棒了。非常感谢!
  • @sgokhales 也许,您实际上希望 rx = r"(?=^((?:{})\b.*?^(?:{})\b))".format("|".join(prefixes), "|".join(suffixes)) 将分隔符作为整个单词进行匹配。看到这个Python demo。
  • 在 cmets 中使用上面的正则表达式,它会给我一个错误:max() arg 是一个空序列。也许,它没有找到任何匹配项。
猜你喜欢
  • 2012-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-31
相关资源
最近更新 更多