Я хотел бы обрезать длинные последовательности одного и того же значения из двоичного файла на Python. Простой способ сделать это - просто прочитать файл и использовать re.sub для замены нежелательной последовательности. Это, конечно, не будет работать с большими двоичными файлами. Можно ли это сделать в чем-то вроде numpy?






Если в памяти поместятся две копии, то вы легко сможете сделать копию. Вторая копия - это сжатая версия. Конечно, вы можете использовать numpy, но вы также можете использовать пакет множество. Кроме того, вы можете рассматривать свой большой двоичный объект как строку байтов и напрямую управлять им.
Похоже, ваш файл может быть В САМОМ ДЕЛЕ большого размера, и вы не можете разместить две копии в памяти. (Вы не предоставили много деталей, так что это всего лишь предположение.) Вам придется выполнять сжатие по частям. Вы будете читать фрагмент, обрабатывать его и записывать. Опять же, numpy, array или простая строка байтов будут работать нормально.
Вам нужно уточнить свой вопрос. Знаете ли вы, какие значения нужно обрезать заранее?
Если вы это сделаете, я бы, вероятно, поискал соответствующие разделы, используя subprocess для запуска "fgrep -o -b <search string>", а затем изменил бы соответствующие разделы файла, используя методы file, seek и read объекта python write.
Если у вас нет памяти для работы с 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
Это не самый эффективный способ, но он будет работать и не требует хранения копии файла в памяти (или двух).
Решение 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()
Эта версия на основе генератора будет сохранять в памяти только один символ содержимого файла за раз.
Обратите внимание, что я понимаю заголовок вашего вопроса буквально - вы хотите сократить количество прогонов одного и того же символ до одного символа. Для замены паттернов вообще не работает:
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()
Предложение 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()
Спасибо, это очень помогает. Я надеялся, что у numpy будет какое-то автоматическое управление памятью для больших файлов - я не слишком знаком с этим.