Это очень солидное начало.У меня есть несколько предложений.
Во-первых, использование 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):
В-третьих, как отметил в комментариях Утдемир, вам не нужно регулярное выражение для этого.Возможно, имеет смысл, если вы хотите, чтобы ваш пользователь мог вводить более сложные поиски, но RE достаточно сложны, чтобы сделать сомнительный пользовательский интерфейс по умолчанию.Я бы просто использовал in
для простого сопоставления подстроки, как в:
if word_search in line:
Остальные операторы в порядке, а в некоторых случаях это лучше всего сделать.Однако часто в ситуациях, когда требуются (скажем) операторы 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