Удаление последовательности символов из большого двоичного файла с использованием Python
-
03-07-2019 - |
Вопрос
Я хотел бы обрезать длинные последовательности одного и того же значения из двоичного файла в Python. Простой способ сделать это - просто прочитать файл и использовать re.sub для замены нежелательной последовательности. Это, конечно, не будет работать с большими двоичными файлами. Это может быть сделано в чем-то вроде numpy?
Решение
Если у вас нет памяти для выполнения open("big.file").read()
, тогда numpy не поможет ... Он использует ту же память, что и переменные python (если у вас 1 ГБ ОЗУ, вы можете загрузить только 1 ГБ данных в 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, но вы также можете использовать массив пакет. Кроме того, вы можете рассматривать ваш большой двоичный объект как строку байтов и манипулировать им напрямую. Р>
Похоже, ваш файл может быть ДЕЙСТВИТЕЛЬНО большим, и вы не можете поместить две копии в память. (Вы не предоставили много подробностей, так что это всего лишь предположение.) Вам придется делать сжатие кусками. Вы будете читать в чанке, делать некоторую обработку этого чанка и записывать его. Опять же, 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()
Вы должны уточнить свой вопрос. Знаете ли вы значения, которые вы хотите обрезать заранее?
Предполагая, что вы это сделаете, я, вероятно, буду искать соответствующие разделы, используя подпроцесс
для запуска " fgrep -o -b < строка поиска >
" а затем измените соответствующие разделы файла, используя методы search
, read
и write
объекта python file
. / р>
Эта версия на основе генератора будет одновременно хранить в памяти ровно один символ содержимого файла.
Обратите внимание, что я буквально воспринимаю заголовок вашего вопроса - вы хотите сократить количество повторений одного и того же символа до одного символа. Для замены шаблонов в целом это не работает:
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()