Подтвердить что ты не робот

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?

4b9b3361

Ответ 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