Python - 在字符串中查找字符串列表的出现

时间:2017-06-01 14:38:23

标签: python string performance search

我有一个大字符串和一个搜索字符串列表,并希望构建一个布尔列表,指示每个搜索字符串是否存在于大字符串中。在Python中执行此操作的最快方法是什么?

下面是一个使用天真方法的玩具示例,但我认为这可能是一种更有效的方法。

e.g。以下示例应返回[1,1,0],因为两者都是"你好"和#34;世界"存在于测试字符串中。

def check_strings(search_list, input):
output = []
for s in search_list:
    if input.find(s) > -1:
        output.append(1)
    else:
        output.append(0)
return output

search_strings = ["hello", "world", "goodbye"] test_string = "hello world" print(check_strings(search_strings, test_string))

4 个答案:

答案 0 :(得分:5)

我不能说这是最快的,(这仍然是O(n * m)),但这是我的方式:

def check_strings(search_list, input_string):
    return [s in input_string for s in search_list]

以下程序可能更快或更快。它使用正则表达式来传递一个输入字符串。请注意,根据您的需要,您可能希望在re.escape(i)表达式中使用re.findall()

def check_strings_re(search_string, input_string):
    import re
    return [any(l)
            for l in
            zip(*re.findall('|'.join('('+i+')' for i in search_string),
                            input_string))]

这是一个完整的测试程序:

def check_strings(search_list, input_string):
    return [s in input_string for s in search_list]


def check_strings_re(search_string, input_string):
    import re
    return [any(l)
            for l in
            zip(*re.findall('|'.join('('+i+')' for i in search_string),
                            input_string))]


search_strings = ["hello", "world", "goodbye"]
test_string = "hello world"
assert check_strings(search_strings, test_string) == [True, True, False]
assert check_strings_re(search_strings, test_string) == [True, True, False]

答案 1 :(得分:4)

使用Aho Corasick算法(https://pypi.python.org/pypi/pyahocorasick/)的实现,它使用单个字符串传递:

import ahocorasick
import numpy as np

def check_strings(search_list, input):
    A = ahocorasick.Automaton()
    for idx, s in enumerate(search_list):
        A.add_word(s, (idx, s))
    A.make_automaton()

    index_list = []
    for item in A.iter(input):
        index_list.append(item[1][0])

    output_list = np.array([0] * len(search_list))
    output_list[index_list] = 1
    return output_list.tolist()

search_strings = ["hello", "world", "goodbye"]
test_string = "hello world"
print(check_strings(search_strings, test_string))

答案 2 :(得分:2)

我发布它只是为了比较。我的比较代码:

#!/usr/bin/env python3
def gettext():
    from os import scandir
    l = []
    for file in scandir('.'):
        if file.name.endswith('.txt'):
            l.append(open(file.name).read())
    return ' '.join(l)

def getsearchterms():
    return list(set(open('searchterms').read().split(';')))

def rob(search_string, input_string):
    import re
    return [any(l)
            for l in
            zip(*re.findall('|'.join('('+i+')' for i in search_string),
                            input_string))]

def blotosmetek(search_strings, input_string):
    import re
    regexp = re.compile('|'.join([re.escape(x) for x in search_strings]))
    found = set(regexp.findall(input_string))
    return [x in found for x in search_strings]

def ahocorasick(search_list, input):
    import ahocorasick
    import numpy as np
    A = ahocorasick.Automaton()
    for idx, s in enumerate(search_list):
        A.add_word(s, (idx, s))
    A.make_automaton()

    index_list = []
    for item in A.iter(input):
        index_list.append(item[1][0])

    output_list = np.array([0] * len(search_list))
    output_list[index_list] = 1
    return output_list.tolist()

def naive(search_list, text):
    return [s in text for s in search_list]

def test(fn, args):
    start = datetime.now()
    ret = fn(*args)
    end = datetime.now()
    return (end-start).total_seconds()

if __name__ == '__main__':
    from datetime import datetime
    text = gettext()
    print("Got text, total of", len(text), "characters")
    search_strings = getsearchterms()
    print("Got search terms, total of", len(search_strings), "words")

    fns = [ahocorasick, blotosmetek, naive, rob]
    for fn in fns:
        r = test(fn, [search_strings, text])
        print(fn.__name__, r*1000, "ms")

我使用Leviathan中显示的不同字词作为搜索字词,并将Project Gutenberg中的25 most downloaded books连接为搜索字符串。结果如下:

Got text, total of 18252025 characters
Got search terms, total of 12824 words
ahocorasick 3824.111 milliseconds
Błotosmętek 360565.542 milliseconds
naive 73765.67 ms

Robs版已经运行了大约一个小时但仍然没有完成。也许它被打破了,也许它只是非常缓慢。

答案 3 :(得分:0)

我的版本使用正则表达式:

def check_strings(search_strings, input_string):
    regexp = re.compile('|'.join([re.escape(x) for x in search_strings]))
    found = set(regexp.findall(input_string))
    return [x in found for x in search_strings]

在原始海报提供的测试数据上,它比Rob的漂亮解决方案慢了一个数量级,但我会对更大的样本做一些基准测试。