Python - механизм для идентификации сжатого типа файла и распаковки
Сжатый файл можно разделить на следующие логические группы
а. Операционная система, над которой вы работаете (* ix, Win) и т.д.
б. Различные типы алгоритмов сжатия (например, zip,.Z,.bz2,.rar,.gzip). По крайней мере, из стандартного списка в основном используемых сжатых файлов.
с. Тогда у нас есть механизм шарового шара - где, я полагаю, нет сжатия. Но это больше похоже на конкатенацию.
Теперь, если мы начнем обращение к указанному выше набору сжатых файлов,
а. Вариант (а) будет выполняться python, поскольку он является независимым от платформы языком.
б. Вариант (b) и (c), похоже, имеет проблему.
Что мне нужно
Как определить тип файла (тип сжатия), а затем UN-compress их?
Вроде:
fileType = getFileType(fileName)
switch(fileType):
case .rar: unrar....
case .zip: unzip....
etc
Итак, основной вопрос заключается в том, как определить алгоритм сжатия на основе файла (если расширение не указано или неверно)? Есть ли какой-либо конкретный способ сделать это в python?
Ответы
Ответ 1
Эта страница содержит список подписи "волшебный". Возьмите те, которые вам нужны, и поместите их в диктофон, как показано ниже. Тогда нам нужна функция, которая соответствует ключам dict с началом файла. Я написал предложение, хотя его можно оптимизировать путем предварительной обработки magic_dict
, например, одно гигантское скомпилированное регулярное выражение.
magic_dict = {
"\x1f\x8b\x08": "gz",
"\x42\x5a\x68": "bz2",
"\x50\x4b\x03\x04": "zip"
}
max_len = max(len(x) for x in magic_dict)
def file_type(filename):
with open(filename) as f:
file_start = f.read(max_len)
for magic, filetype in magic_dict.items():
if file_start.startswith(magic):
return filetype
return "no match"
Это решение должно быть кросс-plattform и, конечно, не зависит от расширения имени файла, но оно может давать ложные срабатывания для файлов со случайным контентом, которые просто начинают с некоторых конкретных магических байтов.
Ответ 2
Основываясь на ответе lazyr и моем комментарии, вот что я имею в виду:
class CompressedFile (object):
magic = None
file_type = None
mime_type = None
proper_extension = None
def __init__(self, f):
# f is an open file or file like object
self.f = f
self.accessor = self.open()
@classmethod
def is_magic(self, data):
return data.startswith(self.magic)
def open(self):
return None
import zipfile
class ZIPFile (CompressedFile):
magic = '\x50\x4b\x03\x04'
file_type = 'zip'
mime_type = 'compressed/zip'
def open(self):
return zipfile.ZipFile(self.f)
import bz2
class BZ2File (CompressedFile):
magic = '\x42\x5a\x68'
file_type = 'bz2'
mime_type = 'compressed/bz2'
def open(self):
return bz2.BZ2File(self.f)
import gzip
class GZFile (CompressedFile):
magic = '\x1f\x8b\x08'
file_type = 'gz'
mime_type = 'compressed/gz'
def open(self):
return gzip.GzipFile(self.f)
# factory function to create a suitable instance for accessing files
def get_compressed_file(filename):
with file(filename, 'rb') as f:
start_of_file = f.read(1024)
f.seek(0)
for cls in (ZIPFile, BZ2File, GZFile):
if cls.is_magic(start_of_file):
return cls(f)
return None
filename='test.zip'
cf = get_compressed_file(filename)
if cf is not None:
print filename, 'is a', cf.mime_type, 'file'
print cf.accessor
Теперь можно получить доступ к сжатым данным с помощью cf.accessor
. Все модули предоставляют аналогичные методы, такие как "read()", "write()" и т.д.).
Ответ 3
Это сложный вопрос, который зависит от ряда факторов: наиболее важным является то, насколько переносимым должно быть ваше решение.
Основы поиска типа файла, заданного для файла, - найти в файле идентификационный заголовок, обычно называемый "magic sequence" или заголовок подписи, который определяет, что файл имеет определенный тип. Его имя или расширение обычно не используются, если его можно избежать. Для некоторых файлов Python имеет это встроенное устройство. Например, чтобы иметь дело с .tar
файлами, вы можете использовать модуль tarfile
, который имеет удобный метод is_tarfile
. Существует аналогичный модуль с именем zipfile
. Эти модули также позволят вам извлекать файлы в чистом Python.
Например:
f = file('myfile','r')
if zipfile.is_zipfile(f):
zip = zipfile.ZipFile(f)
zip.extractall('/dest/dir')
elif tarfile.is_tarfile(f):
...
Если ваше решение - только Linux или OSX, есть также команда file
, которая сделает для вас большую часть работы. Вы можете также использовать встроенные инструменты для распаковки файлов. Если вы просто делаете простой script, этот метод проще и даст вам лучшую производительность.
Ответ 4
"a" полностью неверно.
"b" можно легко интерпретировать плохо, так как ".zip" не означает, что файл на самом деле является zip файлом. Это может быть JPEG с расширением zip (для запутывающих целей, если вы хотите).
Вам действительно нужно проверить, соответствуют ли данные внутри файла данным, которые, как ожидается, имеют расширение.
Также посмотрите магический байт.
Ответ 5
Если упражнение должно идентифицировать его только для файлов меток, у вас есть много ответов. Если вы хотите распаковать архив, почему бы вам просто не попробовать и не поймать ошибки/исключения? Например:
>>> tarfile.is_tarfile('lala.txt')
False
>>> zipfile.is_zipfile('lala.txt')
False
>>> with bz2.BZ2File('startup.bat','r') as f:
... f.read()
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
IOError: invalid data stream