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中都能正常工作,从而对更多人有用。