Как я могу сказать, что файл SVG, не используя магический номер?

Файл SVG по сути является файлом XML, поэтому я мог бы использовать строку <?xml (или шестнадцатеричное представление: '3c 3f 78 6d 6c') в качестве магического числа, но есть несколько противоположных причин не делать этого, если, например, есть лишние пробелы, которые могут сломаться. этот чек.

Другие изображения, которые мне нужно/ожидаю проверить, являются двоичными и имеют магические числа. Как я могу быстро проверить, является ли файл форматом SVG, не используя расширение, в конечном итоге используя Python?


person Eduard Florinescu    schedule 28.02.2013    source источник
comment
как насчет чтения начала файла как двоичного — если вы не можете найти никаких магических чисел, вы читаете его как текст и пытаетесь сопоставить его с вашими известными текстовыми шаблонами?   -  person dmg    schedule 28.02.2013
comment
@DJV Звучит разумно. И я не понимаю, как это могло не сломаться.   -  person Eduard Florinescu    schedule 28.02.2013


Ответы (3)


XML не обязательно должен начинаться с преамбулы <?xml, поэтому проверка этого префикса не является хорошим методом обнаружения, не говоря уже о том, что он идентифицирует каждый XML как SVG. Достойным обнаружением, и его действительно легко реализовать, является использование реального синтаксического анализатора XML для проверки того, что файл является правильно сформированным XML, содержащим элемент верхнего уровня svg:

import xml.etree.cElementTree as et

def is_svg(filename):
    tag = None
    with open(filename, "r") as f:
        try:
            for event, el in et.iterparse(f, ('start',)):
                tag = el.tag
                break
        except et.ParseError:
            pass
    return tag == '{http://www.w3.org/2000/svg}svg'

Использование cElementTree гарантирует эффективность обнаружения благодаря использованию expat; timeit показывает, что файл SVG был обнаружен как таковой примерно через 200 мкс, а не-SVG — через 35 мкс. iterparse API позволяет синтаксическому анализатору отказаться от создания всего дерева элементов (независимо от имени модуля) и читать только начальную часть документа, независимо от общего размера файла.

person user4815162342    schedule 28.02.2013
comment
Прочитав вопрос, смешивание двоичных магических чисел и XML вызвало красное предупреждение. Этот ответ ясно показывает, что для синтаксического анализа двоичного формата требуется один подход, а для чтения (текстового) XML требуется СОВЕРШЕННО ДРУГОЙ подход. - person heltonbiker; 22.04.2013
comment
@heltonbiker Точно. У магических чисел есть одна вещь: грубая производительность. Вот почему в ответ включен пример кода, демонстрирующий эффективную реализацию предлагаемого подхода. - person user4815162342; 22.04.2013
comment
Кроме того, если я правильно понял, двоичный файл по своей сути неструктурирован, например, обычный текстовый файл. Таким образом, в простой текст мы должны включать шебанги, доктипы и т. д., тогда как двоичные файлы нуждаются в этих кратких, загадочных магических числах. В этом смысле я считаю, что эти магические числа напоминают низкоуровневый старый способ хранения данных в файлах минимально возможного размера, в то время как XML и JSON являются более современными, удобочитаемыми, завышенный и избыточный способ хранения данных в файлах. Таким образом, оба подхода отличаются более чем в одном аспекте. - person heltonbiker; 22.04.2013
comment
Из документов: изменено в версии 3.3: этот модуль используйте быструю реализацию, когда это возможно. Модуль xml.etree.cElementTree устарел. - person djvg; 02.04.2021
comment
Мне это нравится, но будьте осторожны: на странице уязвимостей XML упоминается уязвимость к миллиарду смеха и подобным атакам. Тестирование подтвердило, что et.iterparse() действительно взрывается. В документации рекомендуется использовать defusedxml. - person djvg; 02.04.2021
comment
Для тех, кто интересуется синтаксисом ожидаемого значения tag: тег xml с svg пространство имен выглядит как <svg xmlns="http://www.w3.org/2000/svg">, а модуль xml расширяет его до {<namespace uri>}<tag name>, как описано в docs, так что это становится '{http://www.w3.org/2000/svg}svg'. - person djvg; 06.04.2021

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

person dmg    schedule 28.02.2013

Это из man file (здесь), для Unix file команда:

Волшебные тесты используются для проверки файлов с данными в определенных фиксированных форматах. Каноническим примером этого является двоичный исполняемый файл... Эти файлы имеют «магическое число», хранящееся в определенном месте рядом с началом файла, которое сообщает операционной системе UNIX, что файл является двоичным исполняемым файлом, и какой из нескольких типов из этого. Понятие «магия» применялось в расширении к файлам данных. Любой файл с некоторым инвариантным идентификатором с небольшим фиксированным смещением в файл обычно можно описать таким образом. ...

(мой акцент)

А вот магия, которую команда file использует для идентификации файла svg (источник ):

...
0       string        \<?xml\ version=
>14     regex         ['"\ \t]*[0-9.]+['"\ \t]*
>>19    search/4096   \<svg         SVG Scalable Vector Graphics image
...
0       string        \<svg         SVG Scalable Vector Graphics image
...

Как описано в man magic, каждая строка соответствует формату <offset> <type> <test> <message>.

Если я правильно понимаю, это ищет буквальное "<?xml version=". Если он найден, он ищет номер версии, как описано в регулярном выражении. Если это найдено, он ищет следующие 4096 байтов, пока не найдет литерал "<svg". Если что-то из этого не удается, он ищет литерал "<svg" в начале файла.

Нечто подобное можно было бы реализовать и на Python.

Обратите внимание, что существует также python-magic, который предоставляет интерфейс для libmagic, используемый Unix file команда.

person djvg    schedule 06.04.2021