有效地检查单词是否与集合中的模式匹配(Python)



我有一组许多简单的globbing模式和完整的单词,比如:

s = set(['ALE', 'BREAD*', 'BREAKFAST*', 'BROTH' ...])

我还列出了一大堆单词。我想检查这个列表中的每个单词是否与a)集合中的globbing模式或b)集合中一个单词匹配。

如果没有球形图案,我只会做一些类似的事情:

for word in words:
    if word in s:
        # do something

但由于该集也包含球形图案,如果我想将"BREADY"与"BREAD*"匹配,它将找不到匹配项

我能想到的唯一方法是使用嵌套的for循环将每个单词与集合中的每个模式进行比较。有没有一种方法可以检查每个单词在集合中是否匹配,而无需将其与集合中的每个元素进行比较?

您应该将要匹配的完整字符串与要匹配的前缀分开存储。对于您的前缀,请将它们进一步划分为等长前缀集(即一组长度为1的前缀,一组长度2的前缀,等等)

fullstrings = set(["BREAKFAST", "LUNCH", "DINNER", ...])
prefixes_by_length = {} # dict of length -> prefix string
...
prefixes_by_length[4] = set(["CORN", "DESK", ...])
prefixes_by_length[5] = set(["BREAD", "TABLE", ...])

完整字符串匹配很简单,只需检查是否为word in fullstrings即可。

对于前缀,您可以分别检查每个长度,从长度1开始到要匹配的最大前缀长度。对于每个长度n,检查是否为word[:n] in prefixes_by_length[n]

如果你有很多前缀,这将比每次循环使用所有前缀有效得多。

for word in words:
    if word in fullstrings:
        "Match! do something"
    for n in prefixes_by_length:
        if word[:n] in prefixes_by_length[n]:
            "Match! do something"

假设OP不想循环。

import re
import fnmatch
s = set(['ALE', 'BREAD*', 'BREAKFAST*', 'BROTH'])
patterns = [re.compile(fnmatch.translate(p)) for p in s]
for word in "BEING PALE I LIKE ALE WITH BREADDY ABROTH FOR BREAKFASTY TREATS AND BROTH".split():
    for pattern in patterns:
        if pattern.match(word):
            print "HIT", word

提供:

HIT ALE
HIT BREADDY
HIT BREAKFASTY
HIT BROTH

假设我们有一个单词列表words和一个搜索列表searches。对于您给出的简单示例,以下内容就足够了。

for word in words:
    for search in searches:
        if search[-1] == "*":
            search = search[:-1]
            if word.lower().startswith(search.lower()):
                yield word
        else:
            if word.lower() == search.lower():
                yield word

最新更新