【问题标题】:Reading words from txt file - Python从 txt 文件中读取单词 - Python
【发布时间】:2017-11-03 13:54:54
【问题描述】:

我开发了一个代码,负责读取 txt 文件中的单词,在我的例子中是“elquijote.txt”,然后使用字典 {key: value} 来显示出现的单词及其出现。

例如对于包含以下单词的文件“test1.txt”:

hello hello hello good bye bye 

我的程序的输出是:

 hello 3
 good  1
 bye   2

该程序具有的另一个选项是,它显示出现次数比我们通过参数引入的数字更多的单词。

如果在shell中,我们输入以下命令“python readingwords.py text.txt 2”, 将显示文件“test1.txt”中包含的单词出现次数超过我们输入的次数,在本例中为 2

输出:

hello 3

现在我们可以引入常用词的第三个参数,例如确定连词,因为它非常通用,我们不希望在我们的字典中显示或介绍。

我的代码工作正常,问题是使用大文件,例如“elquijote.txt”需要很长时间才能完成。

我一直在思考,这是因为我使用我的辅助列表来消除单词。

我认为作为一种解决方案,不在我的列表中引入那些出现在由参数输入的 txt 文件中的单词,其中包含要丢弃的单词。

这是我的代码:

def contar(aux):
  counts = {}
  for palabra in aux:
    palabra = palabra.lower()
    if palabra not in counts:
      counts[palabra] = 0
    counts[palabra] += 1
  return counts

def main():

  characters = '!?¿-.:;-,><=*»¡'
  aux = []
  counts = {}

  with open(sys.argv[1],'r') as f:
    aux = ''.join(c for c in f.read() if c not in characters)
    aux = aux.split()

  if (len(sys.argv)>3):
    with open(sys.argv[3], 'r') as f:
      remove = "".join(c for c in f.read())
      remove = remove.split()

    #Borrar del archivo  
    for word in aux:  
      if word in remove:
        aux.remove(word) 

  counts = contar(aux)

  for word, count in counts.items():
    if count > int(sys.argv[2]):
      print word, count

if __name__ == '__main__':
    main()

Contar 函数引入字典中的单词。

主函数在“辅助”列表中引入那些不包含符号字符的单词,然后从同一个列表中删除从另一个 .txt 文件加载的那些“禁止”单词。

我认为正确的解决方案是丢弃那些我丢弃不被接受的符号的禁用词,但是在尝试了几种方法后我没有设法正确地做到这一点。

您可以在这里在线测试我的代码: https://repl.it/Nf3S/54 谢谢。

【问题讨论】:

  • 为什么不使用 collections.Counter 进行正常的字数统计,然后消除不需要的字词?将慢速代码移动到较小的音量循环中。
  • 您有内存问题吗? “elquijote.txt”可能是一个很长的文件。如果是整本书,它有 381.104 个单词,来自 22.939 个不同的单词和超过 200 万个字符。批量处理这本书应该是个好主意。

标签: python dictionary


【解决方案1】:

以下是一些优化:

  • 使用 collections.Counter() 计算 contar() 中的项目数
  • 使用 string.translate() 删除不需要的字符
  • 计数后从忽略单词列表中弹出项目,而不是从原始文本中剥离它们。

稍微加快速度,但不是一个数量级。

#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
import os
import collections  

def contar(aux):
    return collections.Counter(aux)

def main():

  characters = '!?¿-.:;-,><=*»¡'
  aux = []
  counts = {}

  with open(sys.argv[1],'r') as f:
    text = f.read().lower().translate(None, characters)
    aux = text.split()

  if (len(sys.argv)>3):
    with open(sys.argv[3], 'r') as f:
      remove = set(f.read().strip().split())
  else:
    remove = []

  counts = contar(aux)
  for r in remove:
    counts.pop(r, None)

  for word, count in counts.items():
    if count > int(sys.argv[2]):
      print word, count

if __name__ == '__main__':
    main()

【讨论】:

    【解决方案2】:

    这里有一些效率低下的地方。我已经重写了您的代码以利用其中一些优化。每次更改的原因都在 cmets / doc 字符串中:

    # -*- coding: utf-8 -*-
    import sys
    from collections import Counter
    
    
    def contar(aux):
        """Here I replaced your hand made solution with the
        built-in Counter which is quite a bit faster.
        There's no real reason to keep this function, I left it to keep your code
        interface intact.
        """
        return Counter(aux)
    
    def replace_special_chars(string, chars, replace_char=" "):
        """Replaces a set of characters by another character, a space by default
        """
        for c in chars:
            string = string.replace(c, replace_char)
        return string
    
    def main():
        characters = '!?¿-.:;-,><=*»¡'
        aux = []
        counts = {}
    
        with open(sys.argv[1], "r") as f:
            # You were calling lower() once for every `word`. Now we only
            # call it once for the whole file:
            contents = f.read().strip().lower()
            contents = replace_special_chars(contents, characters)
            aux = contents.split()
    
        #Borrar del archivo
        if len(sys.argv) > 3:
            with open(sys.argv[3], "r") as f:
                # what you had here was very ineffecient:
                # remove = "".join(c for c in f.read())
                # that would create an array or characters then join them together as a string.
                # this is a bit silly because it's identical to f.read():
                # "".join(c for c in f.read()) === f.read()
                ignore_words = set(f.read().strip().split())
                """ignore_words is a `set` to allow for very fast inclusion/exclusion checks"""
                aux = (word for word in aux if word not in ignore_words)
    
        counts = contar(aux)
    
        for word, count in counts.items():
            if count > int(sys.argv[2]):
                print word, count
    
    
    if __name__ == '__main__':
        main()
    

    【讨论】:

    • 看起来我们的想法非常相似,但你打败了我。
    • 确实,但我很高兴:您无意中向我介绍了一种新方法:translate()。我不确定我会在这里使用它(取决于数据:标点符号错误/标点符号周围缺少间距会破坏它)但我绝对可以找到它的位置。干杯!
    • 我在示例中非常天真地使用了translate 以使其保持简单,但是如果这是所需的功能,您可以创建一个转换表,将列出的字符交换为空格而不是删除它们。
    【解决方案3】:

    一些变化和推理:

    1. __name__ == 'main' 下解析命令行参数: 通过这样做,您可以强制执行代码的模块化,因为它只在您运行此脚本本身时询问命令行参数,而不是从另一个脚本导入函数。
    2. 使用正则表达式过滤掉包含您不想要的字符的单词: 使用正则表达式可以让您说出您想要的字符或您不想要的字符,以较短者为准。在这种情况下,与在简单的正则表达式模式中声明您想要的字符相比,对您不想要的每个特殊字符进行硬编码是一项相当繁琐的任务。在以下脚本中,我使用[aA-zZ0-9]+ 模式过滤掉非字母数字的单词。
    3. 在允许之前请求宽恕:由于最小计数命令行参数是可选的,它显然并不总是存在。因此,我们可以通过使用try except 块尝试将最小计数定义为sys.argv[2] 并捕获IndexError 的异常以将最小计数默认为0

    Python 脚本:

    # sys
    import sys
    # regex
    import re
    
    def main(text_file, min_count):
        word_count = {}
    
        with open(text_file, 'r') as words:
            # Clean words of linebreaks and split
            # by ' ' to get list of words
            words = words.read().strip().split(' ')
    
            # Filter words that are not alphanum
            pattern = re.compile(r'^[aA-zZ0-9]+$')
            words = filter(pattern.search,words)
    
            # Iterate through words and collect
            # count
            for word in words:
                if word in word_count:
                    word_count[word] = word_count[word] + 1
                else:
                    word_count[word] = 1
    
        # Iterate for output
        for word, count in word_count.items():
            if count > min_count:
                print('%s %s' % (word, count))
    
    if __name__ == '__main__':
        # Get text file name
        text_file = sys.argv[1]
    
        # Attempt to get minimum count
        # from command line.
        # Default to 0
        try:
            min_count = int(sys.argv[2])
        except IndexError:
            min_count = 0
    
        main(text_file, min_count)
    

    文本文件:

    hello hello hello good bye goodbye !bye bye¶ b?e goodbye
    

    命令:

    python script.py text.txt
    

    输出:

    bye 1
    good 1
    hello 3
    goodbye 2
    

    使用最小计数命令:

    python script.py text.txt 2
    

    输出:

    hello 3
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-03-17
      • 2013-06-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多