用 std::ifstream 二进制模式读前12–16字节,检查 gcount() 防截断,用 memcmp 精准比对常见 magic number(如 png/jpeg/zip),避免硬编码或通用抽象,优先覆盖高频格式。

怎么读取文件前几个字节判断类型
直接用 std::ifstream 以二进制模式打开,read() 前 16 字节(够覆盖绝大多数 Magic Number)就行。别用文本模式,会因换行符或 EOF 提前截断;也别一上来就读全文件——既慢又没必要。
- 用
std::ios::binary标志打开,否则 Windows 下可能把\x0D\x0A当成单个换行,破坏原始字节对齐 - 读取长度建议至少 12 字节:PNG 是
\x89\x50\x4E\x47\x0D\x0A\x1A\x0A(8 字节),JPEG 是\xFF\xD8\xFF(3 字节),但某些格式(如 ELF、PDF)需要看到更靠后的标识位 - 记得检查
gcount()返回值,文件小于预期长度时不能硬比——比如空文件或只有 2 字节的损坏 JPEG
常见 Magic Number 怎么比对才不翻车
别写一堆 if (buf[0] == 0x89 && buf[1] == 0x50 && ...)。Magic Number 长度不一、有偏移、还带可变字段(比如 ZIP 的 EOCD 位置不固定),硬编码容易漏判或误判。
- PDF 的 Magic 是
%PDF-,但它不一定在开头——有些 PDF 前面有垃圾注释(如%!PS-Adobe-3.0),得从头扫描到第一个%PDF-或跳过注释行 - ZIP 和 APK 都是
\x50\x4B\x03\x04开头,但严格说要结合后续字段(如压缩方法、文件名长度)才能排除极少数伪造情况 - ELF 文件第 5 字节标识 32/64 位(
\x01/\x02),第 6 字节是字节序(\x01=little),只比前 4 字节会把 big-endian ELF 当成别的东西
用 file 命令原理做 C++ 实现靠谱吗
Linux 的 file 命令靠的是魔数数据库(/usr/share/misc/magic),它支持通配、偏移跳转、条件嵌套,C++ 里照搬太重。真要高兼容,不如调用系统 file -b --mime-type 并解析 stdout,比自己维护魔数表现实得多。
- 自己实现完整 magic 匹配逻辑,很快就会遇到“某个格式在不同版本中 Magic 微调”问题(比如新版本 PNG 加了辅助块,开头字节不变但语义扩展)
- 如果必须纯 C++,优先覆盖你实际处理的格式(比如只收 PNG/JPEG/GIF/ZIP),每种写一个轻量函数,用
std::memcmp()+ 明确长度比对,别试图抽象成通用“MagicMatcher”类 - 注意 MIME 类型和文件扩展名不是一回事:一个
.txt文件开头是\x50\x4B\x03\x04,就是 ZIP,不是文本——扩展名只是提示,Magic 才是铁证
Windows 上读 Magic Number 有什么特别要注意的
Windows API 默认用当前代码页解释文件名和内容,但 Magic Number 是二进制,跟代码页完全无关。唯一陷阱是路径字符串本身——如果你用 std::string 存含中文的路径,又传给 std::ifstream,在 VS2015+ 默认 UTF-8 模式下可能出错。
- 路径用
std::wstring+std::wifstream更稳妥,或确保编译时定义_CRT_SECURE_NO_WARNINGS并用_wfopen()底层打开 - 别依赖
GetFileType()或SHGetFileInfo():它们查的是扩展名或 shell 关联,不是真实内容 - UWP 或沙盒环境里,直接 open 文件可能被拒绝——先确认
ifstream::is_open()返回false是权限问题还是路径不存在,别默认归为“不是目标类型”
Magic Number 判断看着简单,实际最麻烦的是边界情况:截断文件、多层封装(比如 JPEG in TIFF)、故意混淆(\x00\x00\x00\x00 开头的无效文件)、还有那些“开头像 A,其实是 B”的格式(如 RARv5 和某些自定义归档)。宁可少支持几种冷门格式,也要把 PNG/JPEG/ZIP 的判断做扎实——毕竟它们占了你日常遇到文件的九成以上。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











