如何在不使用魔术数字的情况下判断文件是否为SVG?
一个 SVG
文件其实就是一个 XML
文件,所以我可以用字符串 <?xml
(或者它的十六进制表示:'3c 3f 78 6d 6c'
)作为一种“魔法数字”来识别它。不过,有几个原因让我不想这么做,比如如果文件里有多余的空格,这种检查就可能失效。
我还需要检查其他一些图片格式,它们都是二进制文件,并且有自己的“魔法数字”。那么,有什么方法可以快速检查一个文件是否是 SVG
格式,而不依赖文件扩展名,最好是用 Python 来实现呢?
3 个回答
以下内容来自于 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
。
你可以尝试把文件的开头当作二进制数据来读取。如果找不到任何特殊的标记(魔法数字),那就把它当作文本文件来读取,看看有没有你想要的文字模式。反过来也可以。
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让解析器可以不必创建整个元素树(虽然模块名是这样),而只读取文档的初始部分,这样不管文件总大小如何,都能提高效率。