我是python的初学者。我有一个巨大的文本文件(数百GB),我想将文件转换为csv文件。在我的文本文件中,我知道行分隔符是一个字符串“<><><><><><><>”。如果一行包含该字符串,我想用“替换它”。有没有办法在不必读取旧文件并重写新文件的情况下执行此操作。
通常我认为我需要做这样的事情:
fin = open("input", "r")
fout = open("outpout", "w")
line = f.readline
while line != "":
if line.contains("<><><><><><><>"):
fout.writeline("\"")
else:
fout.writeline(line)
line = f.readline
但复制数百GB是浪费。另外我不知道open是否会占用大量内存(它是否将文件处理程序视为流?)
非常感谢任何帮助。
注意:该文件的示例是
file.txt
<><><><><><><>
abcdefeghsduai
asdjliwa
1231214 ""
<><><><><><><>
将是csv中的一行和一列。
答案 0 :(得分:5)
@理查德-莱维塞尔
我同意,sed
似乎是正确的方法。以下是对OP所描述内容的粗略描述:
sed -i -e's/<><><><><><><>/"/g' foo.txt
这将在现有foo.txt
中就地替换。出于这个原因,我建议将原始文件置于某种版本控制之下;任何DVCS都应符合要求。
答案 1 :(得分:4)
是的,open()将文件视为流,readline()也是如此。它只会读下一行。但是,如果你调用read(),它会将所有内容读入内存。
乍一看,您的示例代码看起来不错。几乎所有解决方案都要求您将文件复制到其他位置。在没有1:1替换的情况下修改文件内容并不容易。
可能使用一些标准的unix实用程序(最有可能是awk和sed)更快,但我缺少提供完整解决方案所需的unix和bash-fu。
答案 2 :(得分:1)
如果你没有备用磁盘,那就太浪费了。也就是说,当它出现问题时修复它。您的解决方案首次尝试是正常的。
这并不浪费内存,因为文件处理程序是一个流。
答案 3 :(得分:1)
只需使用file iterator:
完成阅读行for line in fin:
if line.contains("<><><><><><><>"):
fout.writeline("\"")
还要考虑CSV writer object来写CSV文件,例如:
import csv
writer = csv.writer(open("some.csv", "wb"))
writer.writerows(someiterable)
答案 4 :(得分:1)
使用python你必须为安全起见创建一个新文件,它会比尝试写入更少的麻烦。
下面列出的是一次读取输入1行并缓冲列(从我对测试输入文件的理解是1行)然后一旦命令行结束,它会将该缓冲区写入磁盘,每1000行原始文件手动刷新一次。这将节省一些IO,而不是写每个段,每次写入32个字节的1000次写入将比8个字节的4000次写入快。
fin = open(input_fn, "rb")
fout = open(output_fn, "wb")
row_delim = "<><><><><><><>"
write_buffer = []
for i, line in enumerate(fin):
if not i % 1000:
fout.flush()
if row_delim in line and i:
fout.write('"%s"\r\n'%'","'.join(write_buffer))
write_buffer = []
else:
write_buffer.append(line.strip())
希望有所帮助。
编辑:忘了提,虽然使用.readline()并不是坏事,不要使用.readlines(),它会将文件的整个内容读入包含每行非常低效的列表中。使用文件对象附带的内置迭代器是最佳的内存使用和速度。答案 5 :(得分:1)
@Constatin表示如果您对
'<><><><><><><>\n'
替换 '" \n'
感到满意
然后替换字符串的长度相同,在这种情况下,您可以使用mmap
制作一个就地编辑的解决方案。你需要python 2.6。以正确的模式打开文件至关重要!
import mmap, os
CHUNK = 2**20
oldStr = ''
newStr = '" '
strLen = len(oldStr)
assert strLen==len(newStr)
f = open("myfilename", "r+")
size = os.fstat(f.fileno()).st_size
for offset in range(0,size,CHUNK):
map = mmap.mmap(f.fileno(),
length=min(CHUNK+strLen,size-offset), # not beyond EOF
offset=offset)
index = 0 # start at beginning
while 1:
index = map.find(oldStr,index) # find next match
if index == -1: # no more matches in this map
break
map[index:index+strLen] = newStr
f.close()
此代码未经过调试!它适用于3 MB测试用例,但可能无法在大型(> 2GB)文件上工作 - mmap
模块似乎仍然有点不成熟,所以我不会过分依赖它。
从更大的角度来看,您发布的内容并不清楚,您的文件最终会被视为有效的CSV。另请注意,您计划用于实际处理CSV的工具可能足够灵活,可以按原样处理文件。
答案 6 :(得分:0)
[完全如所述的问题]如果不用python或任何其他语言复制数据,就无法做到这一点。如果您的处理总是用等长的新子串替换子串,也许您可以就地执行此操作。但是,只要用<><><><><><><>
替换"
,就会改变文件中所有后续字符的位置。从一个地方复制到另一个地方是处理这个问题的唯一方法。
编辑:
请注意,使用sed
实际上不会保存任何复制... sed也不会就地编辑。来自GNU sed manual:
-i [SUFFIX]
--in就地[= SUFFIX]
此选项指定要就地编辑文件。 GNU sed通过创建临时文件并将输出发送到此文件而不是标准输出来完成此操作。
(强调我的。)
答案 7 :(得分:0)
如果您使用双引号分隔字段,看起来您需要转义元素中出现的双引号(例如1231214 ""
需要\n1231214 \"\"
)。
像
这样的东西fin = open("input", "r")
fout = open("output", "w")
for line in fin:
if line.contains("<><><><><><><>"):
fout.writeline("\"")
else:
fout.writeline(line.replace('"',r'\"')
fin.close()
fout.close()