我想从 python 中的二进制文件中修剪相同值的长序列。一种简单的方法是简单地读取文件并使用 re.sub 替换不需要的序列。这当然不适用于大型二进制文件。可以用 numpy 之类的方法完成吗?
6 回答
如果你没有内存要做open("big.file").read()
,那么 numpy 不会真正有帮助。它使用与 python 变量相同的内存(如果你有 1GB 的 RAM,你只能将 1GB 的数据加载到 numpy 中)
解决方案很简单 - 分块读取文件.. f = open("big.file", "rb")
,然后执行一系列f.read(500)
,删除序列并将其写回另一个文件对象。几乎如何在 C 中进行文件读/写。
那么问题是如果您错过了要替换的模式。例如:
target_seq = "567"
input_file = "1234567890"
target_seq.read(5) # reads 12345, doesn't contain 567
target_seq.read(5) # reads 67890, doesn't contain 567
显而易见的解决方案是从文件中的第一个字符开始,检查len(target_seq)
字符,然后前进一个字符,再次检查。
例如(伪代码!):
while cur_data != "":
seek_start = 0
chunk_size = len(target_seq)
input_file.seek(offset = seek_start, whence = 1) #whence=1 means seek from start of file (0 + offset)
cur_data = input_file.read(chunk_size) # reads 123
if target_seq == cur_data:
# Found it!
out_file.write("replacement_string")
else:
# not it, shove it in the new file
out_file.write(cur_data)
seek_start += 1
这并不是最有效的方法,但它会起作用,并且不需要在内存中保留文件的副本(或两个)。
如果两个副本适合内存,那么您可以轻松制作副本。第二份是压缩版。当然,你可以使用 numpy,但你也可以使用array包。此外,您可以将大型二进制对象视为一串字节并直接对其进行操作。
听起来您的文件可能真的很大,并且您无法将两个副本放入内存中。(您没有提供很多细节,所以这只是一个猜测。)您必须分块进行压缩。您将读取一个块,对该块进行一些处理并将其写出。同样,numpy、数组或简单的字节串都可以正常工作。
dbr 的解决方案是一个好主意,但有点过于复杂,您真正需要做的就是在读取下一个块之前将文件指针倒回您正在搜索的序列的长度。
def ReplaceSequence(inFilename, outFilename, oldSeq, newSeq):
inputFile = open(inFilename, "rb")
outputFile = open(outFilename, "wb")
data = ""
chunk = 1024
while 1:
data = inputFile.read(chunk)
data = data.replace(oldSeq, newSeq)
outputFile.write(data)
inputFile.seek(-len(oldSequence), 1)
outputFile.seek(-len(oldSequence), 1)
if len(data) < chunk:
break
inputFile.close()
outputFile.close()
AJMayorga 的建议很好,除非替换字符串的大小不同。或者替换字符串在块的末尾。
我这样修复它:
def ReplaceSequence(inFilename, outFilename, oldSeq, newSeq):
inputFile = open(inFilename, "rb")
outputFile = open(outFilename, "wb")
data = ""
chunk = 1024
oldSeqLen = len(oldSeq)
while 1:
data = inputFile.read(chunk)
dataSize = len(data)
seekLen= dataSize - data.rfind(oldSeq) - oldSeqLen
if seekLen > oldSeqLen:
seekLen = oldSeqLen
data = data.replace(oldSeq, newSeq)
outputFile.write(data)
inputFile.seek(-seekLen, 1)
outputFile.seek(-seekLen, 1)
if dataSize < chunk:
break
inputFile.close()
outputFile.close()
你需要让你的问题更精确。您是否知道要提前修剪的值?
subprocess
假设你这样做,我可能会使用运行“ ”搜索匹配的部分fgrep -o -b <search string>
,然后使用 pythonfile
对象的seek
,read
和write
方法更改文件的相关部分。
这个基于生成器的版本将在内存中一次只保留文件内容的一个字符。
请注意,我是从字面上理解您的问题标题-您希望将同一字符的运行减少为单个字符。对于一般替换模式,这不起作用:
import StringIO
def gen_chars(stream):
while True:
ch = stream.read(1)
if ch:
yield ch
else:
break
def gen_unique_chars(stream):
lastchar = ''
for char in gen_chars(stream):
if char != lastchar:
yield char
lastchar=char
def remove_seq(infile, outfile):
for ch in gen_unique_chars(infile):
outfile.write(ch)
# Represents a file open for reading
infile = StringIO.StringIO("1122233333444555")
# Represents a file open for writing
outfile = StringIO.StringIO()
# Will print "12345"
remove_seq(infile, outfile)
outfile.seek(0)
print outfile.read()