1

在我的数据分析中,我通常必须处理各种 ascii 文件(使用空格或逗号分隔值),以及我经常压缩的大文件,尤其是如果它们最终以svn. 有没有办法编写一个anyOpen()函数来从文件名中计算出 zip 状态并以适当的方式打开它?

4

2 回答 2

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=NORMALpython 库,使用external=PROCESSexternal gzipbzip2xzprocess;,external=PARALLEL并行版本pigzpbzip2使用。如果没有找到外部命令,该函数将退回到 python 库(xz但不是 for )。

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

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

或者

ssv_data = util.anyOpen('!cat foo.csv | tr "," " "')

您可能想知道为什么需要外部解压缩过程?有几个原因:(1)现在几乎每个 CPU 都有一个以上的内核,并且外部解压缩过程使您的 Python 代码使用完整内核处理该文件的 I/O。文件的加速尤其明显,.bz2因为 zip/unzip 非常慢。(2) Pythongzipbz模块不支持解压缩由pigz和创建的多流文件pbzip2。(3) 文件不支持 Python .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

如果 Python 中存在可执行文件,which()函数取自测试吗?例子...

对所有许多案件的清理将是最受欢迎的。快乐的数据monkeying!

于 2013-09-07T16:31:28.937 回答
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
于 2018-01-15T14:54:29.387 回答