Python 如何从CSV的任意BZ2流中读取行?

Python 如何从CSV的任意BZ2流中读取行?,python,python-2.7,csv,bz2,Python,Python 2.7,Csv,Bz2,bz2模块提供了一个标准的open()方法,用户可以通过该方法调用readline()。然而,我的情况是,我有一个流(指向大量数据),我想动态地从中解压行。我当前的实现如下,但我知道必须有一种更简洁的方法来实现这一点 import bz2 import csv BZ2_BUFFER = '' BZ2_DECOMPRESSOR = None BZ2_FILE = None BZ2_READ_SIZE = 100 * 1024 def bz2_csv_rows(fp): glob

bz2
模块提供了一个标准的
open()
方法,用户可以通过该方法调用
readline()
。然而,我的情况是,我有一个流(指向大量数据),我想动态地从中解压行。我当前的实现如下,但我知道必须有一种更简洁的方法来实现这一点

import bz2
import csv

BZ2_BUFFER = ''

BZ2_DECOMPRESSOR = None

BZ2_FILE = None

BZ2_READ_SIZE = 100 * 1024


def bz2_csv_rows(fp):
    global BZ2_BUFFER, BZ2_DECOMPRESSOR, BZ2_FILE, BZ2_READ_SIZE

    BZ2_BUFFER = ''
    BZ2_DECOMPRESSOR = bz2.BZ2Decompressor()
    BZ2_FILE = fp

    for row in csv.reader(iter(bz2_line_reader, b'')):
        yield row


def bz2_line_reader():
    global BZ2_BUFFER, BZ2_DECOMPRESSOR, BZ2_FILE, BZ2_READ_SIZE

    if BZ2_BUFFER is None:
        return None

    while '\n' not in BZ2_BUFFER:
        bindata = BZ2_FILE.read(BZ2_READ_SIZE)

        try:
            data = BZ2_DECOMPRESSOR.decompress(bindata)
        except EOFError:
            break
        except IOError:
            pass

        BZ2_BUFFER += data

        if len(data) < BZ2_READ_SIZE:
            BZ2_FILE = None
            break

    i = BZ2_BUFFER.find('\n')
    if i is None or i < 0:
        line = BZ2_BUFFER
        BZ2_BUFFER = None
        return line

    line = BZ2_BUFFER[:i]
    BZ2_BUFFER = BZ2_BUFFER[i + 1:]
    return line
导入bz2
导入csv
BZ2_缓冲区=“”
BZ2_减压器=无
BZ2_文件=无
BZ2_读取_大小=100*1024
def bz2_csv_行(fp):
全局BZ2_缓冲区、BZ2_解压器、BZ2_文件、BZ2_读取大小
BZ2_缓冲区=“”
BZ2_减压器=BZ2.BZ2减压器()
BZ2_文件=fp
对于csv.reader(iter(bz2线读卡器,b'')中的行:
产量行
def bz2_线_读卡器():
全局BZ2_缓冲区、BZ2_解压器、BZ2_文件、BZ2_读取大小
如果BZ2_缓冲区为无:
一无所获
当'\n'不在BZ2_缓冲区中时:
bindata=BZ2_FILE.read(BZ2_read_大小)
尝试:
数据=BZ2_解压器。解压(bindata)
除EOFError外:
打破
除IOError外:
通过
BZ2_缓冲区+=数据
如果len(数据)

想法?

这里有一些更简洁的东西,而且(在我看来)更具可读性,并且消除了代码使用的所有讨厌的全局变量:

import bz2
import csv
from functools import partial

class BZ2_CSV_LineReader(object):
    def __init__(self, filename, buffer_size=4*1024):
        self.filename = filename
        self.buffer_size = buffer_size

    def readlines(self):
        with open(self.filename, 'rb') as file:
            for row in csv.reader(self._line_reader(file)):
                yield row

    def _line_reader(self, file):
        buffer = ''
        decompressor = bz2.BZ2Decompressor()
        reader = partial(file.read, self.buffer_size)

        for bindata in iter(reader, b''):
            block = decompressor.decompress(bindata).decode('utf-8')
            buffer += block
            if '\n' in buffer:
                lines = buffer.splitlines(True)
                if lines:
                    buffer = '' if lines[-1].endswith('\n') else lines.pop()
                    for line in lines:
                        yield line

if __name__ == '__main__':

    bz2_csv_filename = 'test_csv.bz2'
    for row in BZ2_CSV_LineReader(bz2_csv_filename).readlines():
        print(row)

也许它会有用:我使用Python3,我有一个很大的csv.bz2文件。 我是这样处理的:

import bz2
import csv

def bz2_csv_rows(fp):
    with bz2.open(fp, mode='rt', newline='') as bzfp:
        for row in csv.reader(bzfp):
            yield row

主要功能是在调用bz2.open()中以文本模式打开流:mode='rt',而不是在二进制模式下手动搜索“\n”。但我不确定这是否适用于非物理文件。

什么是BZ2FILE,或者从哪里获得它?我知道,解压缩流上的io.TextIOWrapper是您所需要的,但我无法理解您是如何获得数据的……感谢代码-这非常棒!要使此代码与Python 3兼容,请将行
block=decompressor.decompresse(biodata)
更改为
block=decompressor.decompresse(bindata.decompresse)(“utf-8”)
@Demitri:不客气……这很好听,而且您的建议看起来很有用,但是因为这个问题被标记为“Python-2.7”,关于这一点,我不会改变我的答案。哦,不,我不希望你这样做,只是为了帮助任何想在Python 3中使用它的人(或者可能再次找到这个答案的未来我)@Demitri:决定将您提到的更改合并到答案中,使其在Python 2和Python 3中都能正常工作,从而对更多人有用。