鉴于我的解决方案似乎有些复杂,我可能做错了。
基本上,我正在尝试在文本流中动态替换字符串(例如open('filename', 'r')
或io.StringIO(text)
)。上下文是我试图让pandas.read_csv()
将“无限”处理为“inf”而不是窒息它。
我不想在内存中啜饮整个文件(它可能很大,即使生成的DataFrame将存在于内存中,也不需要整个文本文件)。效率是一个问题。因此,我希望继续使用read(size)
作为获取文本的主要方式(无readline
这是非常慢的)。困难来自read()
可能返回一个文本块的情况,这些文本块在我们要替换的字符串之一的中间结束。
无论如何,下面是我到目前为止所得到的。它处理我到目前为止所抛出的条件(行大于大小,在某些读取块的边界处搜索字符串),但我想知道是否有更简单的东西。
哦,顺便说一下,我除了打电话给read()
之外别处其他任何事情。
class ReplaceIOFile(io.TextIOBase):
def __init__(self, iobuffer, old_list, new_list):
self.iobuffer = iobuffer
self.old_list = old_list
self.new_list = new_list
self.buf0 = ''
self.buf1 = ''
self.sub_has_more = True
def read(self, size=None):
if size is None:
size = 2**16
while len(self.buf0) < size and self.sub_has_more:
eol = 0
while eol <= 0:
txt = self.iobuffer.read(size)
self.buf1 += txt
if len(txt) < size:
self.sub_has_more = False
eol = len(self.buf1) + 1
else:
eol = self.buf1.rfind('\n') + 1
txt, self.buf1 = self.buf1[:eol], self.buf1[eol:]
for old, new in zip(self.old_list, self.new_list):
txt = txt.replace(old, new)
self.buf0 += txt
val, self.buf0 = self.buf0[:size], self.buf0[size:]
return val
示例:
text = """\
name,val
a,1.0
b,2.0
e,+Infinity
f,-inf
"""
size = 4 # or whatever -- I tried 1,2,4,10,100,2**16
with ReplaceIOFile(io.StringIO(text), ['Infinity'], ['inf']) as f:
while True:
buf = f.read(size)
print(buf, end='')
if len(buf) < size:
break
输出:
name,val
a,1.0
b,2.0
e,+inf
f,-inf
所以对于我的申请:
# x = pd.read_csv(io.StringIO(text), dtype=dict(val=np.float64)) ## crashes
x = pd.read_csv(ReplaceIOFile(io.StringIO(text), ['Infinity'], ['inf']), dtype=dict(val=np.float64))
输出:
name val
0 a 1.000000
1 b 2.000000
2 e inf
3 f -inf