压缩一大堆相似的字符串

时间:2013-10-17 20:50:42

标签: python algorithm compression

我正在查看一个维护大量字符串的python脚本。字符串实际上是回归测试运行中记录文件名的完整路径。文件数量太大,脚本开始达到虚拟地址空间限制。

那些字符串很少有熵,所以我认为压缩它们会很好用。问题是我考虑的压缩库(如在Python - Compress Ascii String中讨论的那些)似乎单独压缩每个sting(存储解压缩所需的所有信息)。常识表明,将整个字符串集压缩为单个全局blob /字典并使用某种简短引用引用单个字符串在空间方面会更加有效。

这是一些澄清这个想法的(伪)代码:

class TestResult:
    def __init__(self):
        self._Id         = None
        ....
        self.__appLogList = []
        return
....
    def setAppLogList(self, currentAppLogList):
        self.__appLogList = currentAppLogList
        # what I want to do here instead is 
        # for each s in currentAppLogList
        # add s to global compressed blob and
        # store reference in __appLogList

    def getAppLogList(self):
        return self.__appLogList
        self.__appLogList = currentAppLogList
        # what I want to do here instead is 
        # currentAppLogList = []
        # for each ref in __appLogList
        # decompress ref into string s and add s to currentAppLogList
        # return currentAppLogList

# for each test
# add new TestResult to result map
# setAppLogList

# for selected tests
# getAppLogList and access logs

这可以使用现有的公开Python库吗?

4 个答案:

答案 0 :(得分:2)

这是我能想到的最简单的事情。它通过仅将每个唯一目录路径存储在字典中来“压缩”数据。任何给定目录中的所有文件都存储在list中。出于测试目的,下面的示例代码只读取由每行一个完整文件路径组成的输入文件。

from collections import defaultdict
import os

directories = defaultdict(list)

with open('log_file_paths.txt') as inf:
    for path in (line.strip() for line in inf):
        dir_path, file_name = os.path.split(path)
        directories[dir_path].append(file_name)

for path in directories:
    print path
    for file_name in directories[path]:
        print '   ', file_name

答案 1 :(得分:2)

这是我之前回答的变体或扩展。主要的区别在于它能够通过利用它们实际上是文件路径以及可能有一个或多个公共子组件的事实来潜在地“压缩”字符串。通过从该信息构建树数据结构来消除该冗余,这意味着对于任何给定级别,每个唯一组件值仅存储一次。树数据结构本身使用本质上是字典的字典来实现。由于它使用了内置的reduce()函数,它的创建应该相对较快。

下面的更新版本现在具有以下属性 - 与之前的版本不同 - 可以对所创建的内容进行腌制,这意味着可以保存并稍后从文件中完整地回读。

就像我的其他answer一样,这并不是你所说的“真正的”压缩,但是我认为做一些类似的事情可能会因你要解决的问题而过度杀戮,因为这会解决你的问题,是相对较快,加上维护,增强和延伸会更加简单。

import collections
import cPickle as pickle  # use C version for performance
import operator
import os

class DefaultDict(collections.defaultdict):
    """  pickle-able defaultdict """
    def __reduce__(self):
        args = (self.default_factory,) if self.default_factory else tuple()
        return type(self), args, None, None, self.iteritems()

def Tree(): return DefaultDict(Tree)
class Leaf(object): pass

def print_tree(d, level=0, indent=' '*4):
    """ Tree structure pretty printer """
    if not d:
        print indent * level + '<empty>'
    else:
        for key, value in sorted(d.iteritems()):
            print indent * level + str(key)
            if isinstance(value, dict):
                print_tree(value, level+1, indent)
            elif not isinstance(value, Leaf):
                print indent * (level+1) + str(value)

# create Tree structure from paths
tree = Tree()
LEAF = Leaf()
with open('log_file_paths.txt') as inf:
    for line in inf:
        path = line.strip().split(os.sep)
        reduce(operator.getitem, path[:-1], tree)[path[-1]] = LEAF
print_tree(tree)

# save tree to a file
with open('file_tree.pk', 'wb') as outf:
    pickle.dump(tree, outf, pickle.HIGHEST_PROTOCOL)

# try reading it back in
del tree
with open('file_tree.pk', 'rb') as inf:
    tree = pickle.load(inf)
print_tree(tree)  # display reconstituted tree

因此,如果输入文件由这些文件路径组成:

tests/first/set1.log
tests/first/set2.log
tests/first/subfirst/set3.log
tests/first/subfirst/set4.log
tests/second/set5.log
tests/second/subsecond/set6.log
tests/second/subsecond/subsubsecond/set7.log
tests/third/set8.log

以下树结构是在内存中创建并显示(以后写入,回读和重新显示)以保存它们的结构:

tests
    first
        set1.log
        set2.log
        subfirst
            set3.log
            set4.log
    second
        set5.log
        subsecond
            set6.log
            subsubsecond
                set7.log
    third
        set8.log

答案 2 :(得分:1)

我发现没有现成的实现,我可以在时间限制内应用问题。目前似乎不存在。以下是我考虑的一些方向。

后缀数组是建议的特里树的当代替代品。后缀数组可用于非常有效地实现子字符串搜索。然而,目前尚不清楚如何将它们用于我的问题。后缀阵列概述:Puglisi,S.J。,Smyth,W.F。,&amp; Turpin,A。H.(2007)。后缀数组构造算法的分类。 ACM计算调查(CSUR),39(2),4。

这项研究直接适用于我的问题:Brisaboa,N.R.,Cánovas,R.,Claude,F.,Martínez-Prieto,M。A.,&amp; Navarro,G。(2011年)。压缩的字符串词典。在实验算法中(第136-147页)。施普林格柏林海德堡。不幸的是,没有实现下载和重用。

基于语法的解析器:Larsson,N。J.,&amp; Moffat,A。(2000)。基于离线字典的压缩。 IEEE,88(11),1722-1732的会议记录。实施可用。所有字符串都需要在内存中才能计算语法。我可以收集前1000个字符串并计算语法;或者我可以完全运行(直到内存故障),记录所有字符串并计算语法。可能的问题:字符串随着时间的推移而发展(名称包括日期/时间)。压缩率不是最佳;除了不清楚如果有一个字符串不符合预先计算的语法会发生什么。也许这样的字符串根本不会被压缩。该解决方案不如先前的研究那么有效,并且仍然需要在编码和测试方面付出相当大的努力。

答案 3 :(得分:1)

现在,我只有一个java解决方案。您可以将其用作独立脚本。请将代码here中的值更改为文件的路径并执行。

您可以根据需要修改搜索方法。该项目还有一个可以根据需要自定义的ant脚本。

例如,如果您要搜索文件名:

  

sipproxy0-DC0-自动节点感知-10.20131021_213407_288.snapshot.log

你必须提供完整的路径,即:

  

C:\ ccview \ aglagole_2_cloudute \ tserver_ddpsf \ siptserver \ CloudUte \单元测试\ worker_log \ aglagole \日志\ sipproxy0-DC0-自动节点感知-10.20131021_213407_288.snapshot.log

为了降低内存使用量,我建议您为每行中的公共部分生成一个哈希值(Google的Guava lib具有相当不错的实现)

  

C:\ ccview \ aglagole_2_cloudute \ tserver_ddpsf \ siptserver \ CloudUte \单元测试\ worker_log \ aglagole \日志

如果您遇到任何内存不足问题,请告诉我。