我有一个巨大的CSV文件,大小超过250GB。我想替换字符"(一无所有)。我觉得它应该很简单,但是文件大小确保没有编辑器打开文件。
我也可以使用python
进行以下代码:
with open(file) as src:
lines = src.read()
print(lines.replace(old_string, new_string))
但是此代码要求该文件在内存中。
一个选项是通过编写替换不需要字符的行来创建另一个文件。但这意味着在磁盘上具有几乎相同大小的磁盘上的两个文件。不幸的是,我在服务器上没有那么多磁盘空间。
因此,有没有办法覆盖行并替换字符而不创建新文件?
一些示例CSV线是:
abc,"('91730', 'd9973')",1
def,"('91210', 'd9943')",1
ghi,"('91670', 'd9293')",1
您可以通过这样的文件行迭代:
with open(file, 'rt') as src:
for line in src:
print(line.replace('"', '').replace('(', ''))
但是我将使用CSV模块的CSVReader。
作为创建第二个文件的妥协,您可以用空格替换所有有问题的字符。这样,文件将保持相同的大小,而不需要重写。Python的translate()
功能很快这样做:
import string
table = string.maketrans('(")', ' ')
block_size = 10000000
start_pos = 0
with open('input.csv', 'r+b') as f_input:
while True:
f_input.seek(start_pos)
block = f_input.read(block_size)
if len(block):
f_input.seek(start_pos)
f_input.write(block.translate(table))
else:
break
start_pos += block_size
这将为您提供一个看起来像:
的输出文件abc, '91730', 'd9973' ,1
def, '91210', 'd9943' ,1
ghi, '91670', 'd9293' ,1
我会建议您只需处理"原样"文件:
import csv
with open('input.csv', 'rb') as f_input:
for row in csv.reader(f_input):
data = re.match(r"('(.*?)', '(.*?)'", row[1]).groups()
row[1] = data[0]
row.insert(1, data[1])
print row
对于您的数据,这将显示:
['abc', 'd9973', '91730', '1']
['def', 'd9943', '91210', '1']
['ghi', 'd9293', '91670', '1']
如果您的唯一选项是编辑到适当的文件,则可以执行以下操作:
- 以二进制模式打开文件
- 在缓冲区中读取一块数据(例如4096字节,即页面大小)
- 从该缓冲区中删除字符,或通过字节写入第二个缓冲区,跳过不需要的字符。
- 然后将第二个缓冲区写入同一打开文件后,将文件指针重新定位到正确的位置(使用
seek()
)。(当然,只有新尺寸,而不是完整的4096字节) - 继续重复直到文件末尾,然后将文件(将新文件大小设置)缩小到新书面数据的大小。
因此,您必须跟踪2个文件位置:当前的read_buffer位置和文件中的当前write_buffer位置,每次读取或写入时,请重新定位文件指针。
这也将在当时的阅读和编写字节上工作,但是我不知道(好)Python正在缓冲数据,因此可以慢慢进行。
缓冲区的一种替代方法是使用内存映射。
我会提供一些示例代码,但我没有python(而且我不太了解Python)。
,但是请确保您首先进行一些较小的测试,因为您不会在问题时留下原始文件的副本。
有关读取二进制文件的示例,请参阅此问题。
除非您使用64位版本的Python,否则我将不依赖seek
能够将指针定位在2或4 GB后面。我很确定它不能在Python 2 32位上使用,因为标准图书馆说(强调我的):
file.seek(offset [,when whOne]): 设置文件的当前位置,类似于stdio的fseek()。
在32位系统上,FSEEK仅采用32位参数...无论如何,fseek
在Python 3中可能是安全的,因为整数是长整数,并且对STDIO FSEEK的引用已从文档中删除 - 但是,但是我强烈建议您两次控制它...
这样,我将尝试两次打开文件,一次以" RB"模式打开文件,以便对其进行读取指针,然后以" R B"模式以其上的写入指针。在这里,它可能会根据OS不起作用,但是许多过程允许单个进程在同一文件上获取多个文件描述符。对于Python2,代码与 @Martinevans的答案没有太大区别:
table = string.maketrans('(")', ' ')
block_size = 10000000
start_pos = 0
with open('input.csv', 'rb') as f_input, open('input.csv', 'r+b') as f_output:
while True:
block = f_input.read(block_size)
if len(block):
f_input.seek(start_pos)
f_output.write(block.translate(table))
else:
break