检查字符串是否包含列表中的至少一个字符串

时间:2014-04-08 18:13:56

标签: python list match

我尝试使用python进行匹配。

我有一个字符串列表(len~3000)和一个文件,我想检查文件中的每一行是否至少有一个字符串。

最直接的方法是逐个检查,但需要时间(不过很长时间)。

有没有办法可以更快地搜索?

例如:

list = ["aq", "bs", "ce"]

if the line is "aqwerqwerqwer"  -> true (since has "aq" in it)
if the line is "qweqweqwe" -> false (has none of "aq", "bs" or "ce")

4 个答案:

答案 0 :(得分:15)

您可以使用anygenerator expression

# Please do not name a list "list" -- it overrides the built-in
lst = ["a", "b", "c"]
if any(s in line for s in lst):
    # Do stuff

以上代码将测试lst中是否可以找到line中的任何项目。如果是,则会运行# Do stuff

参见下面的演示:

>>> lst = ["aq", "bs", "ce"]
>>> if any(s in "aqwerqwerqwer" for s in lst):
...     print(True)
...
True
>>> if any(s in "qweqweqwe" for s in lst):
...     print(True)
...
>>>

答案 1 :(得分:1)

这实际上是使用带有自动创建的正则表达式的正则表达式引擎的一个很好的用例。

尝试:

def re_match(strings_to_match, my_file):
    # building regular expression to match
    expression = re.compile(
        '(' + 
        '|'.join(re.escape(item) for item in strings_to_match) +
        ')')

    # perform matching
    for line in my_file:
        if not expression.search(line):
            return False
    return True

正则表达式比每个字符串的简单线性扫描更快,以匹配每一行。这有两个原因:正则表达式是用C实现的,正则表达式被编译成一个状态机,它只检查每个输入字符一次,而不是天真解决方案中的几次。

在IPython笔记本中查看比较:http://nbviewer.ipython.org/gist/liori/10170227。测试数据由3000个字符串组成,以匹配100万行的列表。天真的方法在我的机器上花了1分46秒,而这个解决方案只有9.97秒。

答案 2 :(得分:0)

您可以使用itertools.groupby:

from itertools import groupby
pats = ['pat', 'pat2', …]
matches = groupby(lines, keyfunc=lambda line:any(pat in line for pat in pats))

如果您的模式都是单个字符串,则可以使用集合进一步优化:

pats = set('abcd')
matches = groupby(lines, keyfunc=pats.intersection)

这将导致类似于

的迭代
[(matched patterns, lines matched),
 (empty list, lines not matched),
 (matched patterns, lines matched),
 …]

(除了它将是一个生成器,而不是一个列表。)这是它的主要逻辑。以下是将预处理的生成器迭代到产品输出的一种方法。

for linegrp in matches:
  for line in matched_pats, linegrp:
    if matched_pats:
      print('"{}" matched because of "{}"'.format(line, matched_pats))
    else:
      print('"{}" did not match')

答案 3 :(得分:0)

更多参与但速度更快:将您的字符串列表预处理为前缀trie。

然后,对于每个文件行,从每个字符位置开始,查看您可以走多远的路径。

如果您保留所有活动尝试的队列,则只需在扫描线时查看每个字符位置一次。您还可以在每个trie节点上包含一个“最小终端深度”计数器,以便在接近字符串末尾时提前截断比较。


更简单的一步是将你的大字符串列表减少为字符串列表的字典,由你要查找的每个字符串的前三个字符索引。

from itertools import count, tee, izip

def triwise(iterable):
    # base on pairwise, from the itertools documentation
    "s -> (s0,s1,s2), (s1,s2,s3), (s2,s3,s4), ..."
    a, b, c = tee(iterable, 3)
    next(b, None)
    next(c, None)
    next(c, None)
    return izip(a, b, c)

class Searcher:
    def __init__(self):
        self.index = {}

    def add_seek_strings(self, strings):
        for s in strings:
            pre = s[:3]
            if pre in self.index:
                self.index[pre].append(s)
            else:
                self.index[pre] = [s]

    def find_matches(self, target):
        offset = -1
        for a,b,c in triwise(target):
            offset += 1
            pre = a+b+c
            if pre in self.index:
                from_here = target[offset:]
                for seek in self.index[pre]:
                    if from_here.startswith(seek):
                        yield seek

    def is_match(self, target):
        for match in self.find_matches(target):
            return True
        return False

def main():
    srch = Searcher()
    srch.add_seek_strings(["the", "words", "you", "want"])

    with open("myfile.txt") as inf:
        matched_lines = [line for line in inf if srch.is_match(line)]

if __name__=="__main__":
    main()