用 std::ifstream 二进制模式读前4字节,校验是否为 0xFF 0xD8 0xFF 后接 0xE0–0xEF、0xDB 等合法段标识,可快速可靠判断 JPG 合法性,无需扩展名或完整解码。

怎么用 C++ 读前几个字节判断 JPG 合法性
直接读文件头 4 字节,看是否为 0xFF 0xD8 0xFF 开头(JPG 固定 SOI 标记),再检查第 4 字节是否为 0xE0、0xE1、0xE2 等常见 APPx 段起始——这是最轻量、最可靠的第一道筛选。
注意:不能只靠扩展名,也不能依赖 fopen 后调用图像库解码(那等于打开了文件并做了完整解析)。
- 用
std::ifstream以std::ios::binary模式打开,seekg(0)定位,read()读 4 字节即可 - 务必检查
rdstate()或good(),部分文件可能权限不足或被锁定,导致读不到字节但不报错 - Windows 下路径含中文时,用
std::filesystem::u8path()+std::ifstream构造,避免std::string路径乱码导致 open 失败
为什么不能只校验 SOI(0xFFD8)就认为是 JPG
SOI 是所有 JPEG 文件的强制起始,但仅凭它无法排除伪造或截断文件。真实场景中,很多“伪 JPG”只是把其他二进制数据头部硬改成 0xFF 0xD8,后面根本不是合法 JPEG 结构。
所以必须向后多看 1–2 字节,确认紧接其后的标记是否属于 JPEG 规范定义的 APP0–APP15(0xE0–0xEF)或 DQT(0xDB)等有效段起始。
立即学习“C++免费学习笔记(深入)”;
-
0xFF 0xD8 0xFF 0x00是非法组合(0x00 不是标准段标识),应直接拒绝 -
0xFF 0xD8 0xFF 0xEE理论上合法(APP14),但实际极少见;更稳妥的做法是接受0xE0–0xEF和0xDB、0xC0、0xC2等常见段 - 某些嵌入缩略图的 RAW 文件(如 CR2)也可能以
0xFF 0xD8开头,但后续结构不符 JPEG —— 这类误判需靠更深层解析,但「快速判断」场景下可忽略
遇到 mmap 或内存映射失败怎么办
有人想用 mmap 避免 read() 系统调用开销,但跨平台成本高、权限限制多,反而容易踩坑。对「判断合法性」这种只需前几十字节的场景,mmap 完全没必要。
- Linux/macOS 下若文件被其他进程独占写入(如正在下载),
mmap可能返回MAP_FAILED,而ifstream::read()仍可能成功读到已写入部分 - Windows 的
CreateFileMapping要求显式指定PAGE_READONLY和FILE_MAP_READ,且对网络路径、符号链接支持差 - 结论:老实用
ifstream,4 字节读取耗时在纳秒级,比任何映射初始化都快
要不要校验 JPEG 的 EOI(0xFFD9)结尾
不用。EOI 是文件末尾标记,要验证它就得读完整个文件,违背「不打开/不加载全文」的前提。而且很多合法 JPG(尤其是流式生成或未关闭写入的)根本没写 EOI。
快速判断的目标是「大概率排除非 JPG」,不是「100% 确认可解码」。真要确保可解码,该用 libjpeg 的 jpeg_read_header() 做轻量解析 —— 但那就属于「打开文件并初始化解码器」了,不符合题设。
- 只靠头部 4 字节 + 段类型校验,准确率已在 99%+(实测常见误判来自加密容器或磁盘坏块)
- 若业务要求更高容错,可在头部校验通过后,再尝试用
libjpeg的jpeg_std_error+jpeg_create_decompress做无解码 header 解析,但必须控制超时和内存上限
真正容易被忽略的是:某些杀毒软件或沙箱会拦截对可疑文件的任意读取,哪怕只读 4 字节,也可能触发访问拒绝。如果程序运行在受限环境,得提前捕获 std::ios_base::failure 并降级处理。



















