我知道有更可行的方法来解决这个问题(数据库:mysql,oracle等...),并且我有一个mysql db文件(KJV Bible),我可以通过PHP代码搜索。但是,我想在 Python 中打开 Bible.txt 文件并搜索某些字符串并返回行和行号。此外,(对我来说是一个挑战)我还想归还找到该行的书(来自平面文件)。我一直在阅读并试图更加熟悉Python。不幸的是,我仍然缺乏有效和高效解决问题的知识和技能。这是我想到的:我认为如果我使用 range 方法来设置章节的开头和结尾(代表行号),我可以为每本书/章节硬编码一个名称(例如.. range( 38, 4805)此范围之间的所有线都是创世记)。这似乎有效;我只尝试了几本书。但代码非常冗长(elif 语句)。有谁知道更有效的方法?下面是我为尝试几本书而编写的代码示例,KJV.txt 文件可能是 obtained from Project Gutenberg .
import os
import sys
import re
word_search = raw_input(r'Enter a word to search: ')
book = open("KJV.txt", "r")
regex = re.compile(word_search)
bibook = ''
for i, line in enumerate(book.readlines()):
result = regex.search(line)
ln = i
if result:
if ln in range(36, 4809):
bibook = 'Genesis'
elif ln in range(4812, 8859):
bibook = 'Exodus'
elif ln in range(8867, 11741):
bibook = 'Leviticus'
elif ln in range(11749, 15713):
bibook = 'Numbers'
template = "\nLine: {0}\nString: {1}\nBook: {2}\n"
output = template.format(ln, result.group(), bibook)
print output
最佳答案
这是一个非常坚实的开始。不过我有一些建议。
首先,您对 readlines
的使用效率有点低。 readlines
从文件中创建一个新的行列表——它将整个文件存储在内存中。但你不必这样做;如果您只想迭代文件中的行,您可以只说 for line in file
,或者根据您的情况:
for i, line in enumerate(book):
或者,如果您确实想要将文件存储在内存中(也许是为了重复搜索),请保存 readlines
的结果。到一个变量:
booklines = book.readlines()
for i, line in enumerate(booklines):
您还可以使用 read
将文本存储为单个字符串。 ,尽管在这种情况下这没有多大帮助,因为您仍然需要拆分它:
booktxt = book.read()
booklines = book.splitlines() #
for i, line in enumerate(booklines)
其次,我想说而不是使用 i
作为索引变量,然后单独保存到 ln
,只需在前面使用一个有意义的变量名称即可。 ln
很好,line_number
更清晰但冗长,lineno
是一个很好的妥协。让我们坚持 ln
在这里,因为我们都知道这意味着什么。
for ln, line in enumerate(book):
第三,正如 utdemir 在评论中指出的那样,您实际上并不需要正则表达式。如果您希望用户能够输入更复杂的搜索,那么这可能是有意义的,但 RE 足够复杂,以至于它们会产生一个有问题的默认 ui。我只会使用 in
对于简单的子字符串匹配,如:
if word_search in line:
其余的 if 语句都可以,在某些情况下,这是最好的做法。然而,通常在需要(例如)case
的情况下陈述,实际上使用字典更好。当然,这里有范围,所以我们必须更聪明一点。
让我们从起始页字典开始。很明显,这应该在循环之前,这样我们就不会每次都重新定义字典。
first_lines = {36: 'Genesis', 4812: 'Exodus', 8867: 'Leviticus', 11749: 'Numbers'}
现在我们必须映射 ln
这些字典值之一。但很有可能ln
不等于上述任何数字,因此我们不能将其直接插入字典中。我们可以使用 for
循环迭代字典键( for key in first_lines
),将前一个键存储在 prev_key
中,测试是否 ln > key
,如果是,则返回 prev_key
。但实际上有一种更好的方法可以用 python 来实现。我们没有编写普通的循环,而是使用内置函数 filter
过滤列表。或列表理解,从列表中删除大于 ln
的值。然后我们找到max
.
first_line = max(filter(lambda l: l < ln, first_lines))
这里first_lines
其行为就像一个无序列表的键;一般来说,您可以像遍历列表一样迭代字典中的键,但需要注意的是键可以采用任何顺序。 lambda
是一种定义短函数的方法:此函数采用 x
作为参数并返回 x < ln
的结果。我们必须这样做,因为 filter
想要一个函数作为它的第一个参数。它返回一个包含 first_lines
中所有值的列表。给出 True
结果。
因为这可能有点难以阅读,尤其是当 lambda
时涉及到,我们最好在这里使用列表理解。对于大多数人来说,列表推导式具有良好的可读性和直观性。
first_line = max([l for l in first_lines if l < ln])
在这种情况下,我们甚至可以省略括号,因为我们将它直接传递给函数。 Python 将其解释为“生成器表达式”,它类似于列表理解,但会动态计算值,而不是将它们预先存储在列表中。
first_line = max(l for l in first_lines if l < ln)
现在要获取这本书的名称,您所要做的就是使用 first_line
作为键:
bibook = first_lines[first_line]
最终结果:
import os
import sys
import re
word_search = raw_input(r'Enter a word to search: ')
book = open("KJV.txt", "r")
first_lines = {36: 'Genesis', 4812: 'Exodus', 8867: 'Leviticus', 11749: 'Numbers'}
for ln, line in enumerate(book):
if word_search in line:
first_line = max(l for l in first_lines if l < ln)
bibook = first_lines[first_line]
template = "\nLine: {0}\nString: {1}\nBook: {2}\n"
output = template.format(ln, word_search, bibook)
print output
关于python - 我正在尝试在文本文件中设置范围,以便将搜索结果与特定章节相关联,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6642563/