【问题标题】:walk directory and count words from all files and subdirectories and accumulate totals遍历目录并计算所有文件和子目录中的单词并累积总数
【发布时间】:2018-08-07 18:59:57
【问题描述】:

你好 stackoverflow 社区!多年来,我一直使用这个社区来完成工作、学校和个人探索的小型一次性项目;然而,这是我发布的第一个问题......所以要小心;)

我正在尝试从目录和所有子目录中读取每个文件,然后使用 Python 将结果累积到一个字典中。现在脚本(见下文)正在根据需要读取所有文件,但每个文件的结果都是单独的。我正在寻求帮助以累积成一个。

代码

import re
import os
import sys
import os.path
import fnmatch
import collections

def search( file ):

    if os.path.isdir(path) == True:
        for root, dirs, files in os.walk(path):
            for file in files:
              #  words = re.findall('\w+', open(file).read().lower())
                words = re.findall('\w+', open(os.path.join(root, file)).read().lower())
                ignore = ['the','a','if','in','it','of','or','on','and','to']
                counter=collections.Counter(x for x in words if x not in ignore)
                print(counter.most_common(10))

    else:
        words = re.findall('\w+', open(path).read().lower())
        ignore = ['the','a','if','in','it','of','or','on','and','to']
        counter=collections.Counter(x for x in words if x not in ignore)
        print(counter.most_common(10))

path = raw_input("Enter file and path")

结果

Enter file and path./dirTest

[('this', 1), ('test', 1), ('is', 1), ('just', 1)]

[('this', 1), ('test', 1), ('is', 1), ('just', 1)]

[('test', 2), ('is', 2), ('just', 2), ('this', 1), ('really', 1)]

[('test', 3), ('just', 2), ('this', 2), ('is', 2), ('power', 1),
('through', 1), ('really', 1)]

[('this', 2), ('another', 1), ('is', 1), ('read', 1), ('can', 1),
('file', 1), ('test', 1), ('you', 1)]

期望的结果 - 示例

[('this', 5), ('another', 1), ('is', 5), ('read', 1), ('can', 1),
('file', 1), ('test', 5), ('you', 1), ('power', 1), ('through', 1),
('really', 2)]

任何指导将不胜感激!

【问题讨论】:

    标签: python word-count os.walk


    【解决方案1】:

    我看到您正在尝试从文件/目录扫描中查找某些关键字并获取出现次数

    基本上你可以获得所有此类事件的列表,然后找到每个类似事件的计数

    def couunt_all(array):
        nodup = list(set(array))
        for i in nodup:
            print(i,array.count(i))        
    
    array = ['this','this','this','is','is']
    print(couunt_all(array))
    out:
    ('this', 3)
    ('is', 2)
    

    【讨论】:

    • 这是一个松散相关问题的解决方案,根本无助于解决 OP 的问题。您还设法使您的解决方案越来越差,将O(n) 工作变成O(n²) 工作。
    【解决方案2】:

    问题在于您的print 语句和Counter 对象的使用。我建议如下。

    ignore = ['the', 'a', 'if', 'in', 'it', 'of', 'or', 'on', 'and', 'to']
    
    def extract(file_path, counter):
        words = re.findall('\w+', open(file_path).read().lower())
        counter.update([x for x in words if x not in ignore])
    
    def search(file):
        counter = collections.Counter()
    
        if os.path.isdir(path):
            for root, dirs, files in os.walk(path):
                for file in files:
                    extract(os.path.join(root, file), counter)
        else:
            extract(path, counter)
    
        print(counter.most_common(10))
    

    您可以将常用的代码行分开。同样os.path.isdir(path) 返回一个布尔值,因此您可以直接将其用于if 条件而不进行比较。

    初步解决方案: 我的解决方案是将所有单词附加到一个list,然后将该列表与Counter 一起使用。这样你就可以用你的结果产生一个输出。

    根据@ShadowRanger 提到的性能影响,您可以直接更新计数器,而不是使用单独的列表。

    【讨论】:

    • 我强烈建议您使用updateing Counter,而不是将所有单词的非唯一list 保留到最后,然后计算它们。如果输入文件具有任何实际大小,则在其中存储所有非唯一字将耗尽 RAM,而仅存储唯一字和计数将使用更少的 RAM。即使它没有耗尽 RAM,它仍然消耗比简单计数所需的更多的内存。
    • @ShadowRanger 感谢您的性能观点。我更新了答案。
    • 我有一个后续问题 - 在此解决方案中添加对 .pdf 文件的支持。在 StackOverflow 上有使用 Python 解析 .pdf 文件的答案(例如:stackoverflow.com/questions/18755412/parse-a-pdf-using-python),但我不确定它是否是与计数器解决方案集成的优雅解决方案(extract(os.path.join(root, file ), 计数器))。
    【解决方案3】:

    看起来您想要一个 Counter 以及最后打印的所有累积统计信息,但您正在为每个文件创建一个 Counter,打印它,然后将其丢弃。您只需要将 Counter 初始化和 printing 移到循环之外,并且每个文件只有 update 的“一个真正的 Counter”:

    def search( file ):
        # Initialize empty Counter up front
        counter = Counter()
        # Create ignore only once, and make it a set, so membership tests go faster
        ignore = {'the','a','if','in','it','of','or','on','and','to'}
        if os.path.isdir(path):  # Comparing to True is anti-pattern; removed
            for root, dirs, files in os.walk(path):
                for file in files:
                    words = re.findall('\w+', open(os.path.join(root, file)).read().lower())
                    # Update common Counter
                    counter.update(x for x in words if x not in ignore)
    
        else:
            words = re.findall('\w+', open(path).read().lower())
            # Update common Counter
            counter.update(x for x in words if x not in ignore)
        # Do a single print at the end
        print(counter.most_common(10))
    

    如果您愿意,可以在此处分解出通用代码,例如:

    def update_counts_for_file(path, counter, ignore=()):
        with open(path) as f:  # Using with statements is good, always do it
            words = re.findall('\w+', f.read().lower())
        counter.update(x for x in words if x not in ignore)
    

    允许您用对已分解代码的调用来替换重复代码,但除非代码变得更加复杂,否则可能不值得分解仅重复两次的两行。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-06-27
      • 1970-01-01
      • 2021-12-18
      • 1970-01-01
      • 2012-06-09
      • 2013-10-09
      • 2021-08-09
      • 2019-01-10
      相关资源
      最近更新 更多