libexif是C++项目中读取JPEG EXIF信息最成熟、最轻量的选择,专为EXIF设计,不依赖OpenCV等大图像库,仅支持JPEG的APP1段标准EXIF结构,不支持PNG/WebP及XMP/IPTC。

用 libexif 读取 JPEG 的 EXIF 数据最直接
libexif 是 C++ 项目中读取 JPEG EXIF 信息最成熟、最轻量的选择,不依赖大而全的图像库(比如 OpenCV 或 ImageMagick),专为 EXIF 设计。它原生 C 接口,C++ 项目里直接 #include <libexif/exif-data.h> 即可调用,编译时链接 -lexif。
注意:libexif 只支持 JPEG(含 JFIF/Exif 格式),对 PNG、WebP、HEIC 等格式完全无效;且不解析 XMP 或 IPTC —— 它只认 JPEG APP1 段里的标准 EXIF 结构。
常见错误现象:exif_data_new_from_file() 返回 NULL,多数是因为传入了非 JPEG 文件、文件路径错误,或文件无 EXIF 段(空 APP1)。
实操建议:
立即学习“C++免费学习笔记(深入)”;
- 先用命令行验证文件是否含 EXIF:
identify -verbose image.jpg | grep -i exif(ImageMagick)或exiftool -s image.jpg | head -5 - 调用前检查文件是否存在且可读:
access(path.c_str(), R_OK) == 0 - 务必检查返回值:
ExifData *ed = exif_data_new_from_file(path.c_str());后立即判空 - 释放资源必须调用
exif_data_unref(ed),否则内存泄漏
提取特定字段如拍摄时间、相机型号要遍历 IFD
EXIF 数据按 IFD(Image File Directory)组织,常见字段分散在 EXIF_IFD_0、EXIF_IFD_EXIF、EXIF_IFD_GPS 中。不能靠“字段名字符串”直接查——libexif 用的是 ExifTag 枚举值和 IFD 分区双重定位。
例如获取拍摄时间(EXIF_TAG_DATE_TIME_ORIGINAL)必须指定 IFD:exif_content_get_entry(content, EXIF_TAG_DATE_TIME_ORIGINAL),而 content 来自 exif_data_get_content(ed, EXIF_IFD_EXIF)。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
容易踩的坑:
- 误以为
EXIF_TAG_MODEL在 IFD0,实际在EXIF_IFD_0;但EXIF_TAG_DATE_TIME_ORIGINAL在EXIF_IFD_EXIF—— 查错 IFD 就拿不到值 - 返回的
ExifEntry*的data字段是原始字节,需用exif_entry_get_value()转成可读字符串(自动处理字节序和编码) - GPS 信息(如经纬度)是 rational 类型,
exif_entry_get_value()返回格式如"39.9042, 116.4074",但原始存储是分子/分母两个 uint32_t,需手动解析
避免 segfault:检查 entry 和 data 的有效性再解码
很多崩溃源于对空指针或非法 tag type 的盲目解码。libexif 不做运行时类型防护,exif_entry_get_value() 对未初始化或 type 为 EXIF_FORMAT_UNDEFINED 的 entry 可能直接 crash。
安全做法是逐层校验:
- 确认
ExifContent*非空,再调用exif_content_get_entry() - 拿到
ExifEntry*后,检查entry->data是否非空、entry->size > 0 - 检查
entry->format是否为预期类型(如EXIF_FORMAT_ASCII或EXIF_FORMAT_SHORT),再决定调用哪个 getter - 对 GPS 等复合字段,优先用
exif_entry_get_value(),而非手撸entry->data—— 它已封装好 rational 解析逻辑
示例片段(获取相机型号):
ExifContent *c0 = exif_data_get_content(ed, EXIF_IFD_0);
ExifEntry *e = exif_content_get_entry(c0, EXIF_TAG_MODEL);
if (e && e->data && e->size) {
char val[256] = {0};
exif_entry_get_value(e, val, sizeof(val)-1);
std::string model(val);
}
C++ 封装时别绕过 libexif 的内存模型
有人想用 RAII 封装 ExifData*,直接在析构里调 exif_data_unref(),这没问题;但若试图把 ExifEntry* 或 ExifContent* 也托管起来,就容易出问题 —— 它们生命周期依附于 ExifData*,不能单独 ref/unref。
更隐蔽的问题是深拷贝:libexif 没有 exif_data_copy(),所有“复制”都得手动遍历 tag 重建,开销大且易漏。除非必要,别尝试克隆 EXIF 数据。
真实项目中容易被忽略的一点:libexif 默认不解析缩略图(THUMBNAIL IFD),即使 JPEG 里有嵌入缩略图,exif_data_new_from_file() 也不会加载它。如需缩略图,得手动解析 APP1 后的 SOI/SOF 段,或换用支持 thumbnail 的库(如 exiv2)。

















