使用 extract 和 libextractor 读取文件元数据
现代文件格式提供了使用描述性信息注释文件内容的功能。 这种发展是由需要找到比仅仅使用文件名更好的数据组织方式所驱动的。 此类元数据的问题在于,它在不同的文件格式中没有以标准化的方式存储。 这使得诸如文件管理器或文件共享应用程序等格式无关的工具难以利用这些信息。 这也导致了大量用于提取元数据的特定于格式的工具,例如 AVInfo、id3edit、jpeginfo 和 Vocoditor。
在本文中,将介绍 libextractor 库和 extract 工具。 libextractor 项目的目标是为从不同文件格式获取元数据提供统一的接口。 libextractor 目前被 evidence 使用,evidence 是 Enlightenment 即将发布的版本的文件管理器,以及 GNUnet,一个匿名的、抗审查的点对点文件共享系统。 extract 工具是该库的命令行界面。 libextractor 在 GNU 通用公共许可证下获得许可。
libextractor 与流行的 file 工具有一些相似之处,后者使用文件中的前几个字节来猜测 MIME 类型。 libextractor 与 file 的不同之处在于,它尝试获取比 MIME 类型更多的信息。 根据文件格式,libextractor 可以获得其他信息,包括用于创建文件的软件名称、作者、描述、专辑标题、图像尺寸或电影时长。
libextractor 通过对许多流行的格式使用特定的解析器代码来实现此信息。 当前列表包括 MP3、Ogg、Real Media、MPEG、RIFF (avi)、GIF、JPEG、PNG、TIFF、HTML、PDF、PostScript、Zip、OpenOffice.org、StarOffice、Microsoft Office、tar、DVI、man、Deb、elf、RPM、asf、asf 以及通用方法,例如 MIME 类型检测。 存在许多其他格式,在更流行的格式中,只有少数专有格式不受支持。
集成对新格式的支持很容易,因为 libextractor 使用插件来收集数据。 libextractor 插件是共享库,通常提供解析一种特定格式的代码。 在本文的末尾,我们将演示如何将对新格式的支持集成到库中。 libextractor 收集从各种插件获得的元数据,并为客户端提供一个由分类和字符序列组成的对列表。 分类用于将元数据组织成诸如标题、创建者、主题和描述之类的类别。
安装 libextractor 最简单的方法是使用许多发行版可用的二进制软件包之一。 在 Debian 下,extract 工具位于单独的软件包 extract 中。 将其他应用程序与 libextractor 编译所需的标头包含在 libextractor0-devel 中。 如果您想从源代码编译 libextractor,则需要异常大的内存:大约 256MB 的系统内存是最低要求,因为 GCC 使用大约 200MB 来编译其中一个插件。 否则,手动编译遵循通常的步骤顺序,如清单 1 所示。
清单 1. 编译 libextractor 需要大约 200MB 的内存。
$ wget http://ovmj.org/libextractor/ ↪download/libextractor-0.4.1.tar.gz $ tar xvfz libextractor-0.4.1.tar.gz $ cd libextractor-0.4.1 $ ./configure --prefix=/usr/local $ make # make install
安装 libextractor 后,可以使用 extract 工具从文档中获取元数据。 默认情况下,extract 工具使用规范的插件集,该插件集由当前版本的 libextractor 支持的所有特定于文件格式的插件以及 mime-type 检测插件组成。 清单 2 显示了Linux Journal 网站的示例输出。
清单 2. 从 HTML 中提取元数据。
$ wget -q https://linuxjournal.cn/ $ extract index.html description - The Monthly Magazine of the Linux Community keywords - linux, linux journal, magazine
如果您是 BibTeX 用户,则 -b 选项可能方便地从已正确配备元数据的文档中自动创建 BibTeX 条目,如清单 3 所示。
清单 3. 如果文档附带大量元数据,则创建 BibTeX 条目可能很简单。
$ wget -q http://www.copyright.gov/legislation/dmca.pdf $ extract -b ~/dmca.pdf % BiBTeX file @misc{ unite2001the_d, title = "The Digital Millennium Copyright Act of 1998", author = "United States Copyright Office - jmf", note = "digital millennium copyright act circumvention technological protection management information online service provider liability limitation computer maintenance competition repair ephemeral recording webcasting distance education study vessel hull", year = "2001", month = "10", key = "Copyright Office Summary of the DMCA", pages = "18" }
另一个有趣的选项是 -B LANG。 此选项加载语言特定的但格式无关的插件之一。 这些插件尝试通过将文档中的字符串与字典进行匹配来查找文档中的纯文本。 如果编译 libextractor 需要 200MB 内存的需求看起来很神秘,答案就在这些插件中。 为了执行快速字典搜索,创建了一个 bloomfilter,它允许快速概率匹配; GCC 发现生成的数据结构有点难以接受。
选项 -B 对于当前未记录或不受支持的格式很有用。 可打印插件通常按顺序打印文档的整个文本。 清单 4 显示了在 Microsoft Word 文档上运行 extract 的输出。
清单 4. 即使格式未知,libextractor 有时也可以获得有用的信息。
$ wget -q http://www.bayern.de/HDBG/polges.doc $ extract -B de polges.doc | head -n 4 unknown - FEE Politische Geschichte Bayerns Herausgegeben vom Haus der Geschichte als Heft der zur Geschichte und Kultur Redaktion Manfred Bearbeitung Otto Copyright Haus der Geschichte München Gestaltung fürs Internet Rudolf Inhalt im. unknown - und das Deutsche Reich. unknown - und seine. unknown - Henker im Zeitalter von Reformation und Gegenreformation.
对于德语使用者来说,这是对文本的相当精确的描述。 目前支持的语言是丹麦语 (da)、德语 (de)、英语 (en)、西班牙语 (es)、意大利语 (it) 和挪威语 (no)。 支持其他语言仅仅是添加适当字符集中的免费词典的问题。 extract 手册页中描述了更多选项; 请参阅man 1 extract.
清单 5 显示了使用 libextractor 的最简程序代码。 编译 minimal.c 需要将选项 -lextractor 传递给 GCC。 EXTRACTOR_KeywordList 是一个简单的链表,包含关键字和关键字类型。 有关加载插件和操作关键字列表的详细信息和其他功能,请参阅 libextractor 手册页,man 3 libextractor。 Java 程序员应该知道,也提供了一个使用 JNI 与 libextractor 通信的 Java 类。
清单 5. minimal.c 显示了最主要的 libextractor 函数的协同工作。
#include <extractor.h> int main(int argc, char * argv[]) { EXTRACTOR_ExtractorList * plugins; EXTRACTOR_KeywordList * md_list; plugins = EXTRACTOR_loadDefaultLibraries(); md_list = EXTRACTOR_getKeywords(plugins, argv[1]); EXTRACTOR_printKeywords(stdout, md_list); EXTRACTOR_freeKeywords(md_list); EXTRACTOR_removeAll(plugins); /* unload plugins */ }
为 libextractor 编写新插件最复杂的事情是为特定格式编写实际的解析器。 然而,基本模式始终相同。 插件库必须命名为 libextractor_XXX.so,其中 XXX 表示插件的文件格式。 该库必须导出方法 libextractor_XXX_extract,其签名如清单 6 所示。
清单 6. 每个 libextractor 插件必须导出的函数的签名。
struct EXTRACTOR_Keywords * libextractor_XXX_extract (char * filename, char * data, size_t size, struct EXTRACTOR_Keywords * prev);
参数 filename 指定正在处理的文件的名称。 data 是指向文件通常 mmap 的内容的指针,size 是文件大小。 大多数插件不使用文件名,而是直接解析数据,首先验证数据的标头是否与特定格式匹配。
prev 是到目前为止由文件的其他插件提取的关键字列表。 该函数应返回更新的关键字列表。 如果格式与插件的预期不符,则返回 prev。 大多数插件都使用诸如 addKeyword(清单 7)之类的函数来扩展列表。
清单 7. 插件使用简单的链表返回元数据。
static void addKeyword (struct EXTRACTOR_Keywords ** list, char * keyword, EXTRACTOR_KeywordType type) { EXTRACTOR_KeywordList * next; next = malloc(sizeof(EXTRACTOR_KeywordList)); next->next = *list; next->keyword = keyword; next->keywordType = type; *list = next; }
addKeyword 的典型用法是在文件格式建立后添加 MIME 类型。 例如,JPEG 提取器(清单 8)检查 JPEG 标头的前几个字节,然后中止或声明该文件为 JPEG。 代码中的 strdup 很重要,因为该字符串稍后将被释放,通常在 EXTRACTOR_freeKeywords() 中。 支持的关键字分类列表,在示例中 EXTRACTOR_MIMETYPE 可以在 extractor.h 标头文件中找到。
清单 8. jpegextractor.c 在解析文件标头后将 MIME 类型添加到列表中。
if ( (data[0] != 0xFF) || (data[1] != 0xD8) ) return prev; /* not a JPEG */ addKeyword(&prev, strdup("image/jpeg"), EXTRACTOR_MIMETYPE); /* ... more parsing code here ... */ return prev;
libextractor 是一个简单的可扩展 C 库,用于从文档中获取元数据。 其插件架构和对格式的广泛支持使其与特定于格式的工具区分开来。 该设计受到 libextractor 无法用于更新元数据的事实限制,而更专业的工具通常支持更新元数据。
本文资源: /article/8207。
Christian Grothoff 于 2000 年毕业于伍珀塔尔大学,获得数学学位。 他目前是普渡大学计算机科学专业的博士生,研究静态程序分析和安全的点对点网络。 作为一名自 1995 年以来的 Linux 用户,他为各种自由软件项目做出了贡献,现在是 GNUnet 的维护者和 libextractor 核心团队的成员。 他的主页可以在 grothoff.org/christian 找到。