使用Python在一个.txt文件中搜索单词或短语列表(并显示上下文)

使用Python在一个.txt文件中搜索单词或短语列表(并显示上下文),python,search,text,Python,Search,Text,基本上如问题所述。我对Python相当陌生,喜欢通过看和做来学习 我想创建一个脚本,在文本文档(例如从新闻文章复制和粘贴的文本)中搜索某些单词或短语。理想情况下,单词和短语列表将存储在单独的文件中 在获得结果时,最好能了解结果的上下文。因此,它可能会在找到的每个搜索词前后打印出文本文件中的50个字符。如果它也能显示搜索词是在哪一行找到的,那就太酷了 任何关于如何编写此代码的指南,甚至代码示例都将不胜感激 从这样的事情开始。这段代码不是您所拥有的规范的精确解决方案,但它是一个很好的起点 impor

基本上如问题所述。我对Python相当陌生,喜欢通过看和做来学习

我想创建一个脚本,在文本文档(例如从新闻文章复制和粘贴的文本)中搜索某些单词或短语。理想情况下,单词和短语列表将存储在单独的文件中

在获得结果时,最好能了解结果的上下文。因此,它可能会在找到的每个搜索词前后打印出文本文件中的50个字符。如果它也能显示搜索词是在哪一行找到的,那就太酷了


任何关于如何编写此代码的指南,甚至代码示例都将不胜感激

从这样的事情开始。这段代码不是您所拥有的规范的精确解决方案,但它是一个很好的起点

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))

  • 在main for循环中,我打开输入文件(作为命令行参数传递),并使用
    enumerate
    内置方法获取行号计数器和实际行
    sys.argv
    是存储命令行参数的数组
    sys.argv[0]
    始终是Python脚本的名称

  • 在循环本身中,我取当前行,将其拆分为单个单词,然后再次创建一组单词。然后,我可以快速获取当前行中的单词集与我要查找的单词集的交点。如果交叉点有一个逻辑
    True
    值(即,如果它不是空的),我会打印行号和行号

尚未解决的问题(由您决定):

  • 单词列表现在已在源代码中硬编码,但打开一个额外的文件(其名称已传入,例如,
    sys.argv[2]
    ),逐个读取单词并将其存储在一组中应该不会太难。请注意,您可以通过其
    add
    update
    方法扩展集合(而不是对列表有效的
    append
    extend

  • 显然,如果您使用短语而不是单词(如其中一条评论中所指出的),则上述方法不起作用。由于我假设您想要学习,并且不需要精确的解决方案,我只想说,如果您在一个集合中有短语,您可以通过说
    any(短语在一行中,短语在一组短语中)
    来检查集合元素是否在一行中。这可以用来代替设置交叉点(当然,在这种情况下,不要将线拆分为单词)

  • 如果要打印点击的上下文,可以使用两个额外变量(例如,
    prev\u line
    next\u line
    )来存储前一行和下一行。在for循环中,您实际上将读取
    下一行
    ,而不是
    ,并且在for循环结束时,您应该注意将
    复制到
    上一行
    ,并将
    下一行
    复制到

  • 跟踪上一行和下一行的一种更具Python风格的方法是创建一个Python生成器函数,该函数生成一个元组,每个元组由i-1项、i项和i+1项组成,每个i项都给定一个iterable(如文件)。不过,这是更高级的东西,因为您对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
    

尽管Python社区中的许多人经常表达对正则表达式的反感,但对于适当的用例来说,正则表达式确实是一个宝贵的工具,其中肯定包括识别单词和短语(多亏了
\b
“单词边界”正则表达式模式中的元素——基于字符串处理的替代方案更是一个问题,例如,
。split()
使用空格作为分隔符,从而使标点符号附加到相邻的单词上,这很麻烦,等等)

如果RE’s还可以,我会推荐如下内容:

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()
第一个参数是要查找单词或短语的文本文件(路径),每行一个,第二个参数是要查找单词或短语的文本文件(路径)。如果需要的话,可以很容易地使大小写搜索不敏感(可能只是选择性地基于命令行选项开关),等等

对不熟悉REs.的读者的一些解释:

模式
项中的
\b
项确保不会出现意外匹配(如果您正在搜索“猫”或“狗”,您将不会看到“目录”或“失败者”的意外命中;您也不会错过“猫,微笑,逃跑”中的命中,因为您认为有“猫”这个词包括逗号;-)

|
项表示
,例如,来自包含内容的文本文件(两行)

这将形成模式
'\bcat\b |\bdog\b'
,该模式将定位“猫”或“狗”(作为独立单词,忽略标点符号,但拒绝较长单词中的点击)


re.escape
转义标点符号,因此标点符号按字面意思进行匹配,而不像re模式中通常具有的特殊含义。

要打开输入文件,需要使用
open
。此外,不需要将行中的单词转换为集合,也可以通过
word\u set.intersection(line.split())在内部完成
@FogleBird@silenghost:谢谢你的评论。我采取了某种“迭代”的方法,而且我是即兴的
cat
dog