0

首先,我知道关于 SO 的 Python 内存错误问题的数量,但到目前为止,没有一个与我的用例相匹配。

我目前正在尝试解析一堆文本文件(约 6k 文件,约 30 GB)并存储每个唯一的单词。是的,我正在建立一个单词表,不,我不打算用它做坏事,这是为了大学。

我将找到的单词列表实现为一个集合(使用创建words = set([]),使用words.add(word)),我只是将每个找到的单词添加到其中,考虑到集合机制应该删除所有重复项。

这意味着我需要永久访问整个集合才能使其正常工作(或者至少我看不到其他选择,因为必须在每次插入时检查整个列表的重复项)。

现在,MemoryError当它使用大约 3.4 GB 的 RAM 时,我正在运行大约 25%。我使用的是 32 位 Linux,所以我知道这个限制来自哪里,而我的电脑只有 4 Gigs 的 RAM,所以即使是 64 位也无济于事。

我知道复杂性可能很糟糕(每次插入可能 O(n),虽然我不知道 Python 集是如何实现的(树?)),但它仍然(可能)更快并且(肯定)内存效率更高而不是将每个单词添加到原始列表并随后删除重复项。

有什么办法可以让它运行吗?我预计大约 6-10 GB 的唯一词,所以使用我当前的 RAM 是不可能的,并且升级我的 RAM 目前是不可能的(一旦我开始让这个脚本在大量文件上松散,就不能很好地扩展) .

我目前唯一的想法是在磁盘上缓存(这会进一步减慢进程),或者将临时集写入磁盘并在之后合并它们,这将花费更多时间,而且复杂性确实很可怕。是否有一种解决方案不会导致可怕的运行时间?

作为记录,这是我的全部来源。由于它仅供个人使用,它非常可怕,但你明白了。

import os
import sys
words=set([])
lastperc = 0
current = 1
argl = 0
print "Searching for .txt-Files..."
for _,_,f in os.walk("."):
    for file in f:
        if file.endswith(".txt"):
            argl=argl+1
print "Found " + str(argl) + " Files. Beginning parsing process..."
print "0%                                              50%                                             100%"
for r,_,f in os.walk("."):
    for file in f:
        if file.endswith(".txt"):
            fobj = open(os.path.join(r,file),"r")
            for line in fobj:
                line = line.strip()
                word, sep, remains = line.partition(" ")
                if word != "":
                    words.add(word)
                word, sep, remains = remains.partition(" ")
                while sep != "":
                    words.add(word)
                    word, sep, remains2 = remains.partition(" ")
                    remains = remains2
                if remains != "":
                    words.add(remains)
            newperc = int(float(current)/argl*100)
            if newperc-lastperc > 0:
                for i in range(newperc-lastperc):
                    sys.stdout.write("=")
                    sys.stdout.flush()
            lastperc = newperc
            current = current+1
print ""
print "Done. Set contains " + str(len(words)) + " different words. Sorting..."
sorteddic = sorted(words, key=str.lower)
print "Sorted. Writing to File"
print "0%                                              50%                                             100%"
lastperc = 0
current = 1
sdicl = len(sorteddic)-1
fobj = open(sys.argv[1],"w")
for element in sorteddic:
    fobj.write(element+"\n")
    newperc = int(float(current)/sdicl*100)
    if newperc-lastperc > 0:
        for i in range(newperc-lastperc):
            sys.stdout.write("=")
            sys.stdout.flush()
    lastperc = newperc
    current = current+1
print ""
print "Done. Enjoy your wordlist."

感谢您的帮助和想法。

4

5 回答 5

3

您可能需要将密钥存储在磁盘上。像Redis这样的键值存储可能符合要求。

于 2012-06-06T15:08:22.513 回答
2

您真的是指 6-10GB 的独特词吗?这是英文文本吗?当然,即使算上专有名词和名称,唯一单词也不应该超过几百万。

无论如何,我要做的是一次处理一个文件,甚至一次处理一个文件的一个部分(例如,100k),为该部分构建一个唯一的单词表。然后将所有集合合并为后处理步骤。

于 2012-06-06T15:08:42.177 回答
1

我尝试的第一件事是将单词限制为小写字符 - 正如 Tyler Eaves 指出的那样,这可能会减少集合大小以适应内存。这是一些非常基本的代码来做到这一点:

import os
import fnmatch
import re

def find_files(path, pattern):
    for root, files, directories in os.walk(path):
        for f in fnmatch.filter(files, pattern):
            yield os.path.join(root, f)

words = set()
for file_name in find_files(".", "*.txt"):
    with open(file_name) as f:
        data = f.read()
    words.update(re.findall("\w+", data.lower()))

还有一些评论:

  1. 我通常会期望字典在一开始会迅速增长。在这个过程的后期应该会发现很少的新单词,所以你的推断可能会严重高估单词列表的最终大小。

  2. 为此,集合非常有效。它们被实现为哈希表,并且添加一个新单词的摊销复杂度为 O(1)。

于 2012-06-06T16:04:31.913 回答
1

我倾向于使用数据库表,但如果您想留在单个框架内,请查看 PyTables:http ://www.pytables.org/moin

于 2012-06-06T15:11:46.797 回答
0

将您的密钥散列到更小、更易于管理的代码空间中。将散列键值到包含具有该散列值的键的文件中。哈希表要小得多,单个密钥文件要小得多。

于 2012-06-06T15:25:54.987 回答