如果需要永久访问,Python内存错误解决方案

如果需要永久访问,Python内存错误解决方案,python,memory,python-2.7,Python,Memory,Python 2.7,首先,我知道到目前为止Python内存错误问题的数量,但到目前为止,没有一个与我的用例相匹配 我目前正试图解析一堆文本文件(~6k文件,约30GB),并存储每个唯一的单词。是的,我正在建立一个词表,不,我不打算用它做坏事,它是为了大学 我将找到的单词列表实现为一个集合(使用words=set([]),与words.add(word))一起使用),我只是将找到的每个单词添加到其中,考虑到集合机制应该删除所有重复的单词 这意味着我需要永久访问整个集合才能工作(或者至少我看不到其他选择,因为在每次插入

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

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

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

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

现在,我的内存使用率约为25%,而我的内存使用率约为3.4GB。我使用的是32位Linux,所以我知道这一限制来自何处,我的电脑只有4千兆内存,所以即使是64位也没用

我知道其复杂性可能很糟糕(每次插入时可能是O(n),尽管我不知道Python集是如何实现的(树?),但它仍然(可能)比将每个单词添加到原始列表中并随后删除重复项更快(肯定)更节省内存

有什么办法让它运行吗?我预计会有6-10GB的独特单词,因此使用我当前的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."

感谢您的帮助和想法。

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

你真的是说6-10GB的独特单词吗?这是英文文本吗?当然,就算算上专有名词和名字,也不应该超过几百万个单词


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

我倾向于使用数据库表,但如果您想保持在一个框架内,请签出PyTables:

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

我要尝试的第一件事是将单词限制为小写字符–正如泰勒·埃夫斯所指出的,这可能会减少设置的大小,使其足以容纳内存。下面是一些非常基本的代码:

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()))
还有几点意见:

  • 我通常期望字典在一开始就快速发展;在这个过程的后期,很少会发现新单词,因此你的推断可能会严重高估单词列表的最终大小

  • 集合在这方面非常有效。它们被实现为哈希表,添加一个新词的摊销复杂性为O(1)


  • 我的预测来自于前25%所使用的空间,很可能更低。而且,正如您在代码中看到的,目前我无法删除特殊字符,所以“hi”,“hi”,“hi”,“hi。。。所有这些都将被视为独特的单词,这将进一步扩大词典。不过,谢谢你的想法,我可能会这么做。你使用
    line.partition(“”
    似乎可以通过使用一个简单的
    line.split()
    来简化它。。。但我可能遗漏了什么。@mgilson是的,看起来是这样。我似乎把代码的那部分搞糟了。你有没有关于这个的散列算法的建议?平均字使用的空间少于32个字符的MD5哈希…@malexmave:请注意,MD5哈希是128位的,因此可以将它们存储在16个字节中。它们通常表示为32字节十六进制字符串。当然,这不会改变你的观察结果——它们不会帮助你。@malexmav我的观点,我不清楚,我指的是一个非唯一的散列,据推测只有足够的位使你的字典缩小到合理的大小。乘法字将散列成重复的代码,从而存储在同一个文件中。F83和许多Forth实现使用16位散列来加快对符号表的字查找。这不是大小的问题,因为您没有存储散列。这就像在电话簿中索引到Ts,但索引函数不是字母,而是多项式。这样你的分组是平衡的。这可能是一种方法,尽管我最终必须打开每个字典文件,删除重复项,然后合并所有的字典文件并有选择地对它们进行排序,这将大大提高运行时间(虽然我想我很难要求一个既不增加运行时间又能保证无内存错误终止的解决方案)。你也可以使用pickle或sqlite3?感谢你的代码和观察。特别是集合的复杂性非常有趣。