一般文件为Python打开,它不关心文件压缩

时间:2013-09-07 16:31:29

标签: python file-io

在我的数据分析中,我通常必须处理所有类型的ascii文件(空格或逗号分隔值),以及我经常压缩的大文件,特别是如果它们最终在svn中。有没有办法编写anyOpen()函数,从文件名中找出zip状态并以适当的方式打开它?

2 个答案:

答案 0 :(得分:3)

是。例如,将下面的示例放在文件util.py中。它实现了一个解决方案,您只需打开文件(用于读取或写入),例如,在

f1 = util.anyOpen('data.txt')
f2 = util.anyOpen('data.txt.gz')
f3 = util.anyOpen('data.txt.bz2')
f3 = util.anyOpen('data.txt.xz')

从命令行读取文件名时非常方便。使用anyOpen(),您无需进行任何案例处理,只需传递文件名,

with util.anyOpen(sys.argv[1]) as f:
    for line in f:
        ...

此外,您可以控制解压缩的位置:使用external=NORMAL python库,使用external=PROCESS外部gzipbzip2或{{1}使用过程;使用xz时,会使用并行版本external=PARALLELpigz。如果没有找到外部命令,该函数将回退到python库(但不是pbzip2)。

添加的功能是通过添加感叹号作为xz参数的第一个字符(类似于Mathematica语法)来打开unix管道的简单方法,即

filename

date = util.anyOpen('!date').readline()

您可能想知道为什么需要外部解压缩过程?有几个原因:(1)现在几乎每个CPU都有一个以上的内核,外部解压缩过程使得Python代码使用完整内核处理此文件的I / O.由于zip / unzip非常慢,因此ssv_data = util.anyOpen('!cat foo.csv | tr "," " "') 文件的加速特别明显。 (2)Python的.bz2gzip模块不支持解压由bzpigz创建的多流文件。 (3)没有Python支持pbzip2文件。

.xz

NORMAL = 0 # use python zip libraries PROCESS = 1 # use (zcat, gzip) or (bzcat, bzip2) PARALLEL = 2 # (pigz -dc, pigz) or (pbzip2 -dc, pbzip2) def anyOpen(filename, mode='r', buff=1024*1024, external=PARALLEL): if 'r' in mode and 'w' in mode: return None if filename.startswith('!'): import subprocess if 'r' in mode: return subprocess.Popen(filename[1:], shell=True, bufsize=buff, stdout=subprocess.PIPE).stdout elif 'w' in mode: return subprocess.Popen(filename[1:], shell=True, bufsize=buff, stdin=subprocess.PIPE).stdin elif filename.endswith('.bz2'): if external == NORMAL: import bz2 return bz2.BZ2File(filename, mode, buff) elif external == PROCESS: if not which('bzip2'): return anyOpen(filename, mode, buff, NORMAL) if 'r' in mode: return anyOpen('!bzip2 -dc ' + filename, mode, buff) elif 'w' in mode: return anyOpen('!bzip2 >' + filename, mode, buff) elif external == PARALLEL: if not which('pbzip2'): return anyOpen(filename, mode, buff, PROCESS) if 'r' in mode: return anyOpen('!pbzip2 -dc ' + filename, mode, buff) elif 'w' in mode: return anyOpen('!pbzip2 >' + filename, mode, buff) elif filename.endswith('.gz'): if external == NORMAL: import gzip return gzip.GzipFile(filename, mode, buff) elif external == PROCESS: if not which('gzip'): return anyOpen(filename, mode, buff, NORMAL) if 'r' in mode: return anyOpen('!gzip -dc ' + filename, mode, buff) elif 'w' in mode: return anyOpen('!gzip >' + filename, mode, buff) elif external == PARALLEL: if not which('pigz'): return anyOpen(filename, mode, buff, PROCESS) if 'r' in mode: return anyOpen('!pigz -dc ' + filename, mode, buff) elif 'w' in mode: return anyOpen('!pigz >' + filename, mode, buff) elif filename.endswith('.xz'): if which('xz'): if 'r' in mode: return anyOpen('!xz -dc ' + filename, mode, buff) elif 'w' in mode: return anyOpen('!xz >' + filename, mode, buff) else: return open(filename, mode, buff) return None 函数取自Test if executable exists in Python?示例...

非常欢迎清理所有这些案件。快乐的数据 - monkeying!

答案 1 :(得分:0)

我提出了比@Darko Vebric更简单的解决方案。我的解决方案只使用python库,因此不支持多线程解压缩。如果效率对你很重要,你应该考虑他的解决方案。如果您只需要简单易用的东西,我认为我的解决方案在实现相同目标的同时不那么复杂。

我已经使用Python 3.5对此进行了测试,但据我所知,一般的想法应该适用于任何Python版本。当然,您必须检查您的Python版本是否支持lzma和with关键字。

import sys
import gzip
import bz2
import lzma

fn = sys.argv[1]
if fn.endswith("gz"):
    anyopen = gzip.open
elif fn.endswith("bz2"):
    anyopen = bz2.open
elif fn.endswith("xz"):
    anyopen = lzma.open
else:
    anyopen = open

with anyopen(fn) as f:
    for line in f:
         # Do something with the lines of the input file