在我的数据分析中,我通常必须处理所有类型的ascii文件(空格或逗号分隔值),以及我经常压缩的大文件,特别是如果它们最终在svn
中。有没有办法编写anyOpen()
函数,从文件名中找出zip状态并以适当的方式打开它?
答案 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
外部gzip
,bzip2
或{{1}使用过程;使用xz
时,会使用并行版本external=PARALLEL
和pigz
。如果没有找到外部命令,该函数将回退到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的.bz2
和gzip
模块不支持解压由bz
和pigz
创建的多流文件。 (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