我有一个包含一些内容的文本文件。我需要经常搜索这些内容。我有以下两个选项,哪一个是最好的(通过更快的执行)?
方法1:
def search_list(search_string):
if search_word in li:
print "found at line ",li.indexOf(search_word)+1
if __name__="__main__":
f=open("input.txt","r")
li=[]
for i in f.readlines():
li.append(i.rstrip("\n"))
search_list("appendix")
方法2:
def search_dict(search_string):
if d.has_key(search_word):
print "found at line ",d[search_word]
if __name__="__main__":
f=open("input.txt","r")
d={}
for i,j in zip(range(1,len(f.readlines())),f.readlines()):
d[j.rstrip("\n")]=i
search_dict("appendix")
答案 0 :(得分:2)
如果你经常这样做,那么第二种方法会更快(你已经构建了类似索引的东西)。
稍微调整一下:
def search_dict(d, search_string):
line = d.get(search_string)
if line:
print "found at line {}".format(line)
else:
print "string not found"
d = {}
with open("input.txt", "r") as f:
for i, word in enumerate(f.readlines(), 1):
d[word.rstrip()] = i
search_dict(d, "appendix")
答案 1 :(得分:2)
对于频繁搜索,字典肯定更好(如果你有足够的内存来存储行号),因为密钥是经过哈希处理并在O(1)操作中查找的。但是,您的实现将无法正常工作。第一个f.readlines()
将耗尽文件对象,您将不会使用第二个f.readlines()
读取任何内容。
您要找的是enumerate
:
with open('data') as f:
d = dict((j[:-1],i) for i,j in enumerate(f,1))
还应该指出,在这两种情况下,如果您使用try/except
,只要找到您正在查找的索引,则执行搜索的功能会更快。 (在第一种情况下,无论如何,它可能会更快,因为in
是一个订单N
操作,因此列表的.index
也是如此。
e.g:
def search_dict(d, search_string):
try:
print "found at line {0}".format(d[search_string])
except KeyError:
print "string not found"
或列表:
def search_list(search_string):
try:
print "found at line {0}".format(li.indexOf(search_word)+1)
except ValueError:
print "string not found"
答案 2 :(得分:1)
我在阅读了eumiro和mgilson的答案之后发表了这篇文章。
如果您在命令行上比较两种方法,我认为您会发现第一个更快。其他答案说第二种方法更快,但它们的前提是你在构建索引后会对文件进行多次搜索。如果从命令行按原样使用它们,则不会。
索引的构建比直接搜索字符串更慢,但是一旦构建了索引,搜索就可以非常快速地完成,从而弥补构建它的时间。如果你只使用一次就浪费了这个额外的时间,因为当程序完成时,索引将被丢弃并且必须在下次运行时重建。您需要在查询之间将创建的索引保留在内存中才能获得回报。
有几种方法可以做到这一点,一种是使守护进程保存索引并使用前端脚本来查询它。在Google上搜索python daemon client communication
之类的内容会为您提供有关实施此功能的指示 - here's one method。
答案 3 :(得分:0)
第一个是O(n);第二个是O(1),但它需要搜索密钥。我会选择第二个。
如果您在文档中进行临时搜索,那么任何一个都无法运行。为此,您需要使用Lucene等解析和索引。
答案 4 :(得分:0)
投入的另一个选择是使用SQLite3提供的FTS ...(未经测试并假设您正在寻找全文,而不是单词或其他类似的东西)
import sqlite3
# create db and table
db = sqlite3.connect(':memory:') # replace with file on-disk?
db.execute('create virtual table somedata using fts4(line)')
# insert the data
with open('yourfile.txt') as fin:
for lineno, line in enumerate(fin):
# You could put in a check here I guess...
if somestring in line:
print lineo # or whatever....
# put row into FTS table
db.execute('insert into somedata (line) values (?)', (line,))
# or possibly more efficient
db.executemany('insert into somedata (line) values (?)', fin)
db.commit()
look_for = 'somestring'
matches = db.execute('select rowid from somedata where line match ?', (look_for,) )
print '{} is on lines: {}'.format(look_for, ', '.join(match[0] for match in matches))
如果您只想要第一行,请将limit 1
添加到查询的末尾。
您还可以查看使用mmap
映射文件,然后使用.find
方法获取字符串的最早偏移量,然后假设它不是-1
(即不是找到 - 比方说123456),然后执行mapped_file [:123456] .count('\ n')+ 1来获取行号。