Removendo uma sequência de caracteres de um arquivo binário grande usando python
-
03-07-2019 - |
Pergunta
Gostaria de cortar longas sequências do mesmo valor de um arquivo binário em python.Uma maneira simples de fazer isso é simplesmente ler o arquivo e usar re.sub para substituir a sequência indesejada.É claro que isso não funcionará em arquivos binários grandes.Isso pode ser feito em algo como numpy?
Solução
Se você não tem memória para fazer open("big.file").read()
, então numpy realmente não vai ajudar ..Ele usa a mesma memória que as variáveis python (se você tiver 1 GB de RAM, poderá carregar apenas 1 GB de dados em numpy)
A solução é simples - leia o arquivo em pedaços. f = open("big.file", "rb")
, então faça uma série de f.read(500)
, remova a sequência e grave-a novamente em outro objeto de arquivo.Praticamente como você faz leitura/gravação de arquivos em C.
O problema então é se você perder o padrão que está substituindo.Por exemplo:
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
A solução óbvia é começar no primeiro caractere do arquivo, verificar len(target_seq)
caracteres, avance um caractere e verifique novamente.
Por exemplo (pseudocódigo!):
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
Não é exatamente a maneira mais eficiente, mas funcionará e não exigirá manter uma cópia do arquivo na memória (ou duas).
Outras dicas
Se duas cópias se encaixam na memória, você poderá facilmente fazer uma cópia. A segunda cópia é a versão compactada. Claro, você pode usar Numpy, mas também pode usar o variedade pacote. Além disso, você pode tratar seu grande objeto binário como uma sequência de bytes e manipulá -lo diretamente.
Parece que seu arquivo pode ser VERDADE Grande, e você não pode encaixar duas cópias na memória. (Você não forneceu muitos detalhes, então isso é apenas um palpite.) Você terá que fazer sua compactação em pedaços. Você lerá em um pedaço, fará algum processamento nesse pedaço e escrevê -lo. Novamente, Numpy, matriz ou cordas simples de bytes funcionarão bem.
A solução do DBR é uma boa ideia, mas um pouco complicado tudo o que você realmente precisa fazer é rebobinar o ponteiro do arquivo a duração da sequência que você está procurando, antes de ler seu próximo pedaço.
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 suggestion is fine unless the sizes of the replacement strings are different. Or the replacement string is at the end of the chunk.
I fixed it like this:
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()
You need to make your question more precise. Do you know the values you want to trim ahead of time?
Assuming you do, I would probably search for the matching sections using subprocess
to run "fgrep -o -b <search string>
" and then change the relevant sections of the file using the python file
object's seek
, read
and write
methods.
This generator-based version will keep exactly one character of the file content in memory at a time.
Note that I am taking your question title quite literally - you want to reduce runs of the same character to a single character. For replacing patterns in general, this does not work:
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()