如何在不使用魔术数字的情况下判断文件是否为SVG?

14 投票
3 回答
8264 浏览
提问于 2025-04-17 17:26

一个 SVG 文件其实就是一个 XML 文件,所以我可以用字符串 <?xml(或者它的十六进制表示:'3c 3f 78 6d 6c')作为一种“魔法数字”来识别它。不过,有几个原因让我不想这么做,比如如果文件里有多余的空格,这种检查就可能失效。

我还需要检查其他一些图片格式,它们都是二进制文件,并且有自己的“魔法数字”。那么,有什么方法可以快速检查一个文件是否是 SVG 格式,而不依赖文件扩展名,最好是用 Python 来实现呢?

3 个回答

1

以下内容来自于 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,它提供了一个接口,可以使用Unix file 命令所用的 libmagic

2

你可以尝试把文件的开头当作二进制数据来读取。如果找不到任何特殊的标记(魔法数字),那就把它当作文本文件来读取,看看有没有你想要的文字模式。反过来也可以。

16

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文件被检测为SVG大约需要200微秒,而一个非SVG文件则只需要35微秒。iterparse这个API让解析器可以不必创建整个元素树(虽然模块名是这样),而只读取文档的初始部分,这样不管文件总大小如何,都能提高效率。

撰写回答