it-swarm.com.ru

Python - механизм для определения типа сжатого файла и распаковки

Сжатый файл может быть классифицирован на следующие логические группы
А. Операционная система, с которой вы работаете (* ix, Win) и т.д.
Б. Различные типы алгоритмов сжатия (т.е. .Zip, .Z, .bz2, .rar, .gzip). Atleast из стандартного списка наиболее часто используемых сжатых файлов.
С. Тогда у нас есть механизм тарного шара - там, где, я полагаю, нет сжатия. Но это больше похоже на конкатенацию. 

Теперь, если мы начнем обращаться к вышеуказанному набору сжатых файлов,
А. Вариант (а) будет заботиться Python, так как это независимый от платформы язык.
Б. Варианты (б) и (в), похоже, имеют проблемы. 

Что мне нужно
Как определить тип файла (тип сжатия), а затем распаковать их? 


Подобно: 

fileType = getFileType(fileName)  
switch(fileType):  
case .rar:  unrar....
case .Zip:  unzip....

etc  

Таким образом, фундаментальный вопрос заключается в том, как определить алгоритм сжатия на основе файла (при условии, что расширение не указано или неверно)? Есть ли конкретный способ сделать это в Python?

24
kumar_m_kiran

На этой странице есть список "волшебных" подписей файлов. Возьмите те, которые вам нужны, и поместите их в диктовку, как показано ниже. Затем нам нужна функция, которая сопоставляет клавиши 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"

Это решение должно быть кросс-платформенным и, разумеется, не зависит от расширения имени файла, но оно может давать ложные срабатывания для файлов со случайным содержимым, которые могут начинаться с определенных магических байтов.

28
Lauritz V. Thaulow

Исходя из ответа ленивца и моего комментария, я имею в виду следующее:

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 () и т.д.

14
Ber

Это сложный вопрос, который зависит от ряда факторов: наиболее важным является то, насколько мобильным должно быть ваше решение.

Основы, лежащие в основе поиска типа файла для данного файла, заключаются в том, чтобы найти в файле идентифицирующий заголовок, обычно называемый «магическая последовательность» или заголовок сигнатуры , который идентифицирует, что файл имеет определенный тип. Его имя или расширение обычно не используется, если его можно избежать. Для некоторых файлов в 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, которая сделает за вас большую работу. Вы также можете использовать встроенные инструменты для распаковки файлов. Если вы просто делаете простой сценарий, этот метод проще и даст вам лучшую производительность.

4
Krumelur

«а» совершенно неверно.

«b» может быть легко истолковано плохо, так как «.Zip» не означает, что файл на самом деле является Zip-файлом. Это может быть JPEG с расширением Zip (если хотите, в замешательстве).

Вам на самом деле нужно проверить, соответствуют ли данные внутри файла тем данным, которые, как ожидается, будут иметь его расширение. Также взгляните на магический байт .

0
alexandernst

Если упражнение состоит в том, чтобы идентифицировать его только для маркировки файлов, у вас есть много ответов. Если вы хотите распаковать архив, почему бы вам просто не попытаться отловить исключения/ошибки? Например:

>>> 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
0
Burhan Khalid