В основном, как говорится в вопросе. Я новичок в Python и люблю учиться, видя и делая.
Я хотел бы создать сценарий, который выполняет поиск в текстовом документе (например, в тексте, скопированном и вставленном из новостной статьи) для определенных слов или фраз. В идеале список слов и фраз должен храниться в отдельном файле.
При получении результатов было бы здорово получить контекст результатов. Так что, возможно, он мог бы распечатать 50 символов в текстовом файле до и после каждого найденного условия поиска. Было бы здорово, если бы он также показывал, в какой строке был найден поисковый запрос.
Любые указатели на то, как кодировать это, или даже примеры кода будут высоко оценены.
Ответ 1 · рейтинг 7 · Alex Martelli
Несмотря на часто выражаемую антипатию к регулярным выражениям со стороны многих в сообществе Python, они действительно являются ценным инструментом для соответствующих случаев использования, которые определенно включают в себя идентификацию слов и фраз (благодаря элементу \b "граница слова" в шаблоны регулярных выражений - альтернативы, основанные на обработке строк, представляют гораздо большую проблему, например, .split() использует пробел в качестве разделителя и, таким образом, досадно оставляет знаки препинания прикрепленными к соседним словам и т. д. и т. д.).
Если RE в порядке, я бы рекомендовал что-то вроде:
import re
import sys
def main():
if len(sys.argv) != 3:
print("Usage: %s fileofstufftofind filetofinditin" % sys.argv[0])
sys.exit(1)
with open(sys.argv[1]) as f:
patterns = [r'\b%s\b' % re.escape(s.strip()) for s in f]
there = re.compile('|'.join(patterns))
with open(sys.argv[2]) as f:
for i, s in enumerate(f):
if there.search(s):
print("Line %s: %r" % (i, s))
main()
первый аргумент - это (путь) текстовый файл со словами или фразами для поиска, по одному в строке, а второй аргумент (путь) - текстовый файл, в котором их нужно найти. При желании легко сделать случай нечувствительным к поиску (возможно, просто опционально на основе переключателя параметров командной строки) и т. д. и т. д.
Некоторое пояснение для читателей, не знакомых с RE...:
Элемент \b в patterns элементах гарантирует, что случайных совпадений не будет (если вы ищете "кошка" или "собака", вы не увидите случайного совпадения с "каталогом" или "аутсайдером"; и вы выиграли не пропустите попадание в "Кошка, улыбнувшись, убежала" по некоторому расщеплению, думая, что слово там "кошка", включая запятую ;-).
Элемент | означает or, поэтому, например. из текстового файла с содержимым (две строки)
cat
dog
это сформирует шаблон '\bcat\b|\bdog\b', который найдет либо "кошку", либо "собаку" (как отдельные слова, игнорируя пунктуацию, но отклоняя совпадения в более длинных словах).
re.escape избегает знаков препинания, поэтому он соответствует буквально, а не со специальным значением, как это обычно имеет место в шаблоне RE.
Ответ 2 · рейтинг 3 · Tamás
Начните с чего-то вроде этого. Этот код не является точным решением для имеющейся у вас спецификации, но является хорошей отправной точкой.
import sys
words = "foo bar baz frob"
word_set = set(words.split())
for line_number, line in enumerate(open(sys.argv[1])):
if words_set.intersection(line.split()):
print "%d:%s" % (line_number, line.strip())
Некоторые пояснения ниже:
Искомые слова изначально хранятся в строке (в строке 3). Я разбиваю этот список слов по пробелам и создаю из него набор, чтобы было легче проверить, есть ли какие-либо слова в текущей строке в списке слов. (Проверка членства в наборе - O(1), а в списке - O(n)).
В основном цикле for я открываю входной файл (который передается как аргумент командной строки) и использую встроенный метод enumerate для получения счетчика номера строки, а также фактической строки. sys.argv - это массив, в котором хранятся аргументы командной строки; sys.argv[0] - это всегда имя скрипта Python.
В самом цикле я беру текущую строку, разбиваю ее на отдельные слова и снова создаю набор из слов. Затем я могу быстро найти пересечение набора слов в текущей строке с набором слов, которые я ищу. Если пересечение имеет логическое значение True (т. е. если оно не пусто), я печатаю номер строки, а также строку.
Вещи, которые еще не решены (и оставлены на ваше усмотрение):
Список слов теперь жестко закодирован в исходнике, но не должно быть слишком сложно открыть дополнительный файл (чье имя передается, скажем, sys.argv[2]), прочитать его слова одно за другим и сохранить их в наборе. Обратите внимание, что вы можете расширять наборы с помощью их методов add и update (вместо append и extend, которые работают для списков).
Очевидно, что описанный выше метод не работает, если вместо слов у вас фразы (как указано в одном из комментариев). Поскольку я предполагаю, что вы хотите учиться и вам не нужно точное решение, я скажу только, что если у вас есть фразы в наборе, вы можете проверить, находится ли какой-либо из элементов набора в строке, сказав any(phrase in line for phrase in set_of_phrases). Это можно использовать вместо заданного пересечения (и, конечно, в этом случае не разбивайте строку на слова).
Если вы хотите распечатать контекст совпадений, вы можете использовать две дополнительные переменные (скажем, prev_line и next_line), в которых хранится предыдущая и следующая строки. В цикле for вы фактически будете читать next_line вместо line, а в конце цикла вам следует позаботиться о копировании line в prev_line и next_line в line.
Еще более питоновский способ отслеживать предыдущую и следующую строки - создать функцию генератора Python, которая выдает кортеж, состоящий из элемента i-1, элемента i и элемент i+1 для каждого i заданного итерируемого объекта (например, файла). Однако это более сложный материал, и, поскольку вы довольно плохо знакомы с Python, я думаю, что лучше оставить его на потом. Однако, если вам любопытно, функция генератора, выполняющая эту задачу, может выглядеть так:
def context_generator(iterable):
prev, current, next = None, None, None
for element in iterable:
prev, current, next = current, next, element
if current is not None:
yield prev, current, next
if next is not None:
yield current, next, None