我有一个大字符串和一个搜索字符串列表,并希望构建一个布尔列表,指示每个搜索字符串是否存在于大字符串中。在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))
答案 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的漂亮解决方案慢了一个数量级,但我会对更大的样本做一些基准测试。