Я пытаюсь установить диапазоны в текстовом файле, чтобы связать результаты поиска с определенной главой. - PullRequest
2 голосов
/ 10 июля 2011

Я знаю, что есть более подходящие подходы к решению этой проблемы (db: mysql, oracle и т. Д.), И у меня есть файл mysql db (KJV Bible), который я могу искать с помощью кода PHP. Тем не менее, я хочу открыть файл Bible.txt в Python, найти определенные строки и вернуть строку и номер строки. Кроме того (вызов для меня), я хочу также вернуть книгу, в которой была найдена строка (из плоского файла). Я читал и пытался познакомиться с Python. К сожалению, мне все еще не хватает знаний и навыков для эффективного и действенного решения проблем. Вот что я придумала: я подумала, что если я использую метод диапазона, чтобы установить начало и конец главы (представляющий номера строк), я могу жестко закодировать имя для каждой книги / главы (например, диапазон) 38, 4805) вся черта между этим диапазоном - Бытие). Это похоже на работу; Я только попробовал несколько книг. Но код очень многословен (утверждения elif). Кто-нибудь знает более эффективный подход? Ниже приведен пример кода, который я написал, чтобы попробовать несколько книг, и файл KJV.txt может быть , полученный из 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

Ответы [ 5 ]

2 голосов
/ 10 июля 2011

Это очень солидное начало.У меня есть несколько предложений.

Во-первых, использование 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
1 голос
/ 10 июля 2011

Простой способ избежать elifs - это цикл. Также гораздо эффективнее проверить, находится ли число в диапазоне с start <= ln < stop, вместо использования - range, возвращающего список, и Python должен сравнивать каждый элемент.

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 = ''

bookranges = [
    ((36, 4809),  'Genesis'),
    ((4812, 8859), 'Exodus'),
    ((8867, 11741), 'Leviticus'),
    ((11749, 15713), 'Numbers')
]


for ln, line in enumerate(book.readlines()):
    result = regex.search(line)
    if result:
        for (start, stop), bibook in bookranges:
            if start <= ln <= stop:
                # found the book, so end the loop and use it later
                break
        else:
            # didnt find any range that matches.
            bibook = 'Somewhere between books'

     template = "\nLine: {0}\nString: {1}\nBook: {2}\n"
     output = template.format(ln, result.group(), bibook)
     print output
1 голос
/ 10 июля 2011

Вы можете попробовать что-то вроде этого. Обратите внимание, что книги появляются одна за другой, поэтому вам нужно только записать, какую именно книгу вы сейчас просматриваете. Кроме того, ваш подход к проверке, находится ли номер строки в range, очень дорогой, поскольку для каждой строки в текстовом файле вы строите каждый диапазон, а затем выполняете линейное сканирование, чтобы увидеть, появляется ли в нем номер строки.

books = [("Introduction",36),("Genesis",4809),("Exodus",8859),
         ("Leviticus",11741),("Numbers",15713)]

import os
import sys
import re

word_search = raw_input(r'Enter a word to search: ')
book = open("KJV.txt", "r")
bookIndex = 0
bookEnd = books[bookIndex][1]

for lineNum, line in enumerate(book):
    if lineNum > bookEnd:
        bookIndex += 1
        bookEnd = books[bookIndex][1]
    if word_search in line:
        template = "\nLine: {0}\nString: {1}\nBook: {2}\n"
        output = template.format(lineNum, line, books[bookIndex][0])
        print output

В одном из комментариев указывалось, что вы, возможно, сможете использовать подход, основанный на данных, а не жестко кодировать позиции книг. Каждая из книг начинается со строки или строк в узнаваемом формате? если это так, вы можете попробовать проверить это и записать текущую книгу, на которую вы смотрите.

1 голос
/ 10 июля 2011
     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'

лучше записать как:

#      (start, end, book)
tab = [(36, 4809, 'Genesis'), 
       (4812, 8859, 'Exodus'),
       (8867, 11741, 'Leviticus'),
       (11749, 15713, 'Numbers')]
for start, end, book in tab:
    if start <= ln < end:
        bibook = book
        break
1 голос
/ 10 июля 2011

Немного изменилась версия вашего кода.

word_search = raw_input(r'Enter a word to search: ')

with open("KJV.txt", "r") as book:
    #using with is always better when messing with files.
    bibook = ''
    for pos, line in enumerate(book):
    #a file object is already an iterable, so i don't think we need readlines.
        if result in line:
        #if result is always in ranges in your question, no need to check other limits.
        #also comparision operators is a lot faster than in.
            if pos < 4809:
                bibook = 'Genesis'
            elif pos < 8859:
                bibook = 'Exodus'
            elif pos < 11741:
                bibook = 'Leviticus'
            else:
                bibook = 'Numbers'
            #you can use string templates, but i think no need for that
            out = "\nLine: {0}\nString: {1}\nBook: {2}".format(
                                            pos, line, book)

            print(out)

Редактировать:

Теперь я читаю ваш файл примера.Я думаю, что лучше было бы разделить первую часть «1: 2» и использовать ее для изучения книги и номера строки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...