不能用 fread 读完整个 jpg 文件解析宽高,因其可能达 gb 级,易 oom;只需定位 sof0(\xff\xc0)段,其后 2 字节长度、1 字节精度、2 字节高、2 字节宽(大端),需跳过 app/com 等可选段,严格按各 marker 长度字段语义偏移。

为什么不能用 fread 读完整个 JPG 文件来解析宽高
因为 JPG 文件可能几百 MB,甚至上 GB(比如扫描件、航拍图),全读进内存不仅慢,还可能直接 OOM。真正需要的只是文件开头几百字节里的 SOF0(Start of Frame 0)段——它紧跟着 SOI(\xFF\xD8)标记,里面明文存着宽高(各 2 字节,大端序)。
怎么定位并读取 SOF0 段中的宽高字段
JPG 是基于标记(marker)的二进制格式,SOF0 标记是 \xFF\xC0,其后紧跟 2 字节长度(含自身),再之后是精度(通常 1 字节)、高度(2 字节)、宽度(2 字节)。关键在于:不能假设 SOF0 一定在第 2 个 marker;中间可能有 APP0(EXIF)、APP1(缩略图)、COM(注释)等可选段,必须逐个跳过。
- 用
fopen(..., "rb")打开文件,fseek到开头 - 先校验前 2 字节是否为
\xFF\xD8(否则不是合法 JPG) - 循环读 marker:
fread2 字节 → 若以\xFF开头且第二字节非\x00和\xFF,即为有效 marker - 若 marker 是
\xFF\xC0,再fread2 字节得 length,然后跳过 1 字节精度,接着读 2 字节高度、2 字节宽度 - 遇到
\xFF\xD9(EOI)或读取超限(如 64KB)就终止,防坏文件卡死
常见错误:把 APP 段长度读错导致偏移错乱
APPn(n=0..15)和 COM 段的长度字段也占 2 字节,但它的「长度」包含这 2 字节本身;而 SOF0 的 length 字段**不包含**前面的 marker 2 字节。很多实现误把 APP 段长度当偏移增量,结果 fseek 错位,后续全乱。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 读到
\xFF\xE0~\xFF\xEF或\xFF\xFE时,必须fread(&len, 1, 2, fp),然后fseek(fp, len - 2, SEEK_CUR) - 读到
\xFF\xC0时,同样fread(&len, 1, 2, fp),但后续数据就在当前位置,无需再 fseek - 别用
fscanf或文本模式打开 —— 二进制里全是\x00,文本模式会提前截断
C++ 实操示例:只读必要字节的安全解析
下面这段代码在真实项目中跑过 20+ GB 的 JPG,平均耗时
bool read_jpg_dims(const char* path, int& w, int& h) {
FILE* fp = fopen(path, "rb");
if (!fp) return false;
uint8_t buf[4];
if (fread(buf, 1, 2, fp) != 2 || buf[0] != 0xFF || buf[1] != 0xD8) {
fclose(fp); return false;
}
while (true) {
if (fread(buf, 1, 2, fp) != 2) break;
if (buf[0] != 0xFF) break;
uint8_t marker = buf[1];
if (marker == 0xD9) break; // EOI
if (marker == 0xC0) { // SOF0
if (fread(buf, 1, 3, fp) != 3) break; // len(2) + precision(1)
if (fread(buf, 1, 4, fp) != 4) break; // h(2) + w(2)
h = (buf[0] = 0xE0 && marker <p>注意:实际部署时建议加 <code>fseek(fp, 0, SEEK_SET)</code> 前的 <code>if (ftell(fp) == -1)</code> 安全检查,某些嵌入式文件系统对大文件 ftell 不稳定。</p>C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










