Как я могу обработать часть файла, как будто это сам файл?
У меня есть данные, хранящиеся в коллекции файлов или в одном составном файле. Составной файл формируется путем объединения всех отдельных файлов, а затем предшествует всему заголовку, в котором указаны смещения и размеры составных частей. Я хотел бы иметь подобный файлу объект, который представляет представление составного файла, где представление представляет только один из файлов участника. (Таким образом, у меня могут быть функции для чтения данных, которые принимают либо реальный файловый объект, либо объект "просмотра", и им не нужно беспокоиться о том, как хранится какой-либо конкретный набор данных.) Какая библиотека будет делать это для меня?
mmap
класс выглядел многообещающе, так как он построен из файла, длины и смещения, и это именно то, что у меня есть, но смещение должно быть выровнено с гранулярностью выделения базовой файловой системы, а файлы, которые я читаю, не соответствуют это требование. Наименование MultiFile
Класс отвечает всем требованиям, но он предназначен для вложений в сообщениях электронной почты, и мои файлы не имеют такой структуры.
Больше всего меня интересуют файловые операции read
, seek
, а также tell
, Файлы, которые я читаю, являются двоичными, поэтому текстовые функции, такие как readline
а также next
не так важны. Я мог бы в конечном счете также нуждаться write
, но я готов пока отказаться от этой функции, так как не знаю, как должен вести себя добавление.
2 ответа
Я знаю, что вы искали библиотеку, но как только я прочитал этот вопрос, я решил написать свою собственную. Итак, вот оно:
import os
class View:
def __init__(self, f, offset, length):
self.f = f
self.f_offset = offset
self.offset = 0
self.length = length
def seek(self, offset, whence=0):
if whence == os.SEEK_SET:
self.offset = offset
elif whence == os.SEEK_CUR:
self.offset += offset
elif whence == os.SEEK_END:
self.offset = self.length+offset
else:
# Other values of whence should raise an IOError
return self.f.seek(offset, whence)
return self.f.seek(self.offset+self.f_offset, os.SEEK_SET)
def tell(self):
return self.offset
def read(self, size=-1):
self.seek(self.offset)
if size<0:
size = self.length-self.offset
size = max(0, min(size, self.length-self.offset))
self.offset += size
return self.f.read(size)
if __name__ == "__main__":
f = open('test.txt', 'r')
views = []
offsets = [i*11 for i in range(10)]
for o in offsets:
f.seek(o+1)
length = int(f.read(1))
views.append(View(f, o+2, length))
f.seek(0)
completes = {}
for v in views:
completes[v.f_offset] = v.read()
v.seek(0)
import collections
strs = collections.defaultdict(str)
for i in range(3):
for v in views:
strs[v.f_offset] += v.read(3)
strs = dict(strs) # We want it to raise KeyErrors after that.
for offset, s in completes.iteritems():
print offset, strs[offset], completes[offset]
assert strs[offset] == completes[offset], "Something went wrong!"
И я написал другой скрипт для генерации файла "test.txt":
import string, random
f = open('test.txt', 'w')
for i in range(10):
rand_list = list(string.ascii_letters)
random.shuffle(rand_list)
rand_str = "".join(rand_list[:9])
f.write(".%d%s" % (len(rand_str), rand_str))
Это сработало для меня. Файлы, на которых я тестировал, не являются бинарными, как ваши, и они не такие большие, как ваши, но я надеюсь, что это может быть полезно. Если нет, то спасибо, это был хороший вызов:D
Кроме того, мне было интересно, если это на самом деле несколько файлов, почему бы не использовать какой-нибудь формат файла архива и использовать их библиотеки для их чтения?
Надеюсь, поможет.
В зависимости от того, насколько сложным вам это должно быть, что-то вроде этого должно работать - я остановился на некоторых деталях, поскольку не знаю, насколько близко вам нужно эмулировать файловый объект (например, будете ли вы когда-либо использовать obj.read()
, или вы всегда будете использовать obj.read(nbytes)
):
class FileView(object):
def __init__(self,file,offset,length):
self._file=file
self._offset=offset
self._length=length
def seek(self,pos):
#May need to get a little fancier here to support the second argument to seek.
return self._file.seek(self._offset+pos)
def tell(self):
return self._file.tell()-self._offset
def read(self,*args):
#May need to get a little more complicated here to make sure that the number of
#bytes read is smaller than the number of bytes available for this file
return self._file.read(*args)