Python:re.findall()对重叠的子字符串不起作用

时间:2019-02-16 13:14:48

标签: python regex

我想将字符串与值列表匹配。它们可以重叠,例如string = "test1 test2"values = ["test1", "test1 test2"]

编辑:以下是我的完整代码,为一个简单的示例

import regex    

string = "This is a test string"
values = ["test", "word", "string", "test string"]

pattern = r'\b({})\b'.format('|'.join(map(regex.escape, values)))
matches = set(map(str.lower, regex.findall(pattern, string, regex.IGNORECASE)))

output = ([x.upper() for x in values if x.lower() in matches])

print(output) # ['TEST', 'STRING']
# Expected output:  ['TEST', 'STRING', 'TEST STRING']

2 个答案:

答案 0 :(得分:1)

正如 Wiktor 所说,如果要查找所有匹配项,则不能 使用替代方法,因为正则表达式处理器会尝试连续的替代方法 并仅返回找到的第一个替代。

因此,您的程序必须对每个值使用单独模式进行测试, 但是出于性能原因,您可以预先编译所有它们。

我发现您的Python安装与我的安装之间的另一个区别 是import regex。显然,您使用了一些较旧的Python版本,例如 我使用import re(版本3.7)。我什至检查了Python版本2.7.15, 使用import re

脚本如下所示:

import re

def mtch(pat, str):
    s = pat.search(str)
    return s.group().upper() if s else None

# Strings to look for
values = ["test", "word", "string", "test string"]
# Compile patterns
patterns = [ re.compile(r'\b({})\b'.format(re.escape(v)),
    re.IGNORECASE) for v in values ]
# The string to check
string = "This is a test string"
# What has been found
list(filter(None, [ mtch(pat, string) for pat in patterns ]))

mtch函数返回pat找到的文本(已编译模式) 匹配中str(源字符串)或 None 中失败。

patterns包含已编译模式的列表。

然后有[ mtch(pat, string) for pat in patterns ]个列表 理解,生成匹配结果列表(具有 None 值 如果匹配尝试失败)。

要过滤掉 None 个值,我使用了filter函数。

最后list收集所有过滤的字符串并打印:

['TEST', 'STRING', 'TEST STRING']

如果要对多个源字符串执行此搜索, 对每个源字符串仅运行最后一个语句,可能会添加 结果(以及搜索到的字符串的某种指示) 到一些结果列表。

如果源列表很长,则不应尝试全部阅读它们。 相反,您应该循环阅读它们并运行检查 仅适用于当前输入字符串。

截至2019年2月18日10:00Z编辑有关评论的内容

我从您的评论中读到,读取字符串的代码如下:

with open("Test_data.csv") as f:
    for entry in f:
        entry = entry.split(',')
        string = entry[2] + " " + entry[3] + " " + entry[6] 

请注意,您在每个循环中都会覆盖string,因此在循环结束后, 您从最后行(仅)获得了结果。

或者也许在阅读完之后,您就可以搜索当前模式 字符串?

另一个提示更改代码:

  1. 避免此类组合,例如entry变量最初成立 整个字符串,然后是列表-拆分结果。 也许更具可读性的变体是:

    for row in f:
        entry = row.split(',')
    
  2. 在读取一行之后,然后再执行其他任何操作,请检查该行是否 刚刚阅读的不为空。如果该行为空,则将其忽略。 一种快速的测试方法是使用if中的字符串(一个空字符串 计算为 False )。

    for row in f:
        if row:
            entry = row.split(',')
            ...
    
  3. string = entry[2] + " " + entry[3] + " " + entry[6]检查之前 entry列表是否至少有7个项目(计数从0开始)。 也许您的某些输入行包含较小个片段 因此您的程序会尝试从不存在的元素中读取 此列表?

  4. 为了确定您要检查的字符串,请编写一个简短程序 仅 会拆分输入并打印结果字符串。然后看看它们,也许您发现问题了。

答案 1 :(得分:0)

如果您确定文本中包含foobar,则无需分别在文本中搜索foobar:您已经知道答案了。

首先对您的搜索进行分组:

searches = ['test', 'word', 'string', 'test string', 'wo', 'wordy']
unique = set(searches)
ordered = sorted(unique, key = len)
grouped = {}

while unique:
    s1 = ordered.pop()
    if s1 in unique:
        unique.remove(s1)
        grouped[s1] = [s1]
        redundant = [s2 for s2 in unique if s2 in s1]
        for s2 in redundant:
            unique.remove(s2)
            grouped[s1].append(s2)

for s, dups in grouped.items():
    print(s, dups)

# Output:
# test string ['test string', 'string', 'test']
# wordy ['wordy', 'word', 'wo']

将所有内容归类后,可以将搜索范围限制为仅顶层搜索(grouped的键)。

此外,如果要考虑规模和性能,您是否真的需要正则表达式?您当前的示例可以使用更快的普通in测试来处理。如果确实需要正则表达式,则对搜索进行分组的想法会比较困难-但在某些情况下可能并非不可能。