【问题标题】:Use Python to search one .txt file for a list of words or phrases (and show the context)使用 Python 在一个 .txt 文件中搜索单词或短语列表(并显示上下文)
【发布时间】:2011-03-01 18:37:22
【问题描述】:

基本上如问题所述。我对 Python 还很陌生,喜欢边看边做来学习。

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

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

任何关于如何编码的指针,甚至是代码示例都将不胜感激。

【问题讨论】:

    标签: python search text


    【解决方案1】:

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

    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])应该不会太难,逐个读取它的单词一个并将它们存储在一组中。请注意,您可以通过它们的 addupdate 方法扩展集合(而不是适用于列表的 appendextend)。

    • 显然,如果您使用短语而不是单词(如其中一个 cmets 中所指出的),上述方法将不起作用。由于我假设您想学习并且不需要精确的解决方案,因此我只会说,如果您在集合中有短语,则可以通过说 any(phrase in line for phrase in set_of_phrases) 来检查是否有任何集合元素在一行中。这可以用来代替设置的交集(当然,在这种情况下不要将你的线分成单词)。

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

    • 还有一种更 Pythonic 的方式来跟踪上一行和下一行,它是创建一个 Python 生成器函数,该函数生成一个由 item i-1、item 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
      

    【讨论】:

    • 打开输入文件你需要使用open
    • 另外你不需要将行中的单词转换成集合,它可以由word_set.intersection(line.split())内部完成
    • @FogleBird @SilentGhost:感谢 cmets。我采取了某种“迭代”方法,并且在发送后我正在改进我的答案。您的建议已包含在我的回答中。至于短语,我不想给出一个确切的开箱即用的解决方案,因为我觉得如果原始发布者自己弄清楚它会更好,只使用我的答案作为指导。我在其中一个要点中提到了短语的情况。
    • 感谢您的详细回复,正是我所需要的——会感兴趣地阅读。
    【解决方案2】:

    尽管 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()
    

    第一个参数是(路径)一个包含要查找的单词或短语的文本文件,每行一个,第二个参数(路径)是要在其中找到它们的文本文件。如果需要,可以很容易地使大小写搜索不敏感(可能只是可选地基于命令行选项开关)等等。

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

    patterns 项目中的 \b 项目确保不会出现意外匹配(如果您正在搜索“cat”或“dog”,您将不会看到“catalog”或“underdog”;而且你不会错过“猫,微笑着,跑掉了”中的一击,因为一些分裂的想法是“猫”,包括逗号;-)。

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

    cat
    dog
    

    这将形成'\bcat\b|\bdog\b' 模式,它将定位“cat”或“dog”(作为独立词,忽略标点符号,但拒绝较长词中的命中)。

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

    【讨论】:

    • 再次感谢 ace 的回复 - 一些带有解释的代码非常有帮助。我想知道 RE,但不确定它在这种情况下是否相关 - 很高兴看到它!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-03-22
    • 1970-01-01
    • 2015-08-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多