必须用 open(path, 'rb') 读取无后缀文件,取前16–32字节比对 Magic Number(如 PNG:b'\x89PNG\r\n\x1a\n',JPEG:b'\xff\xd8\xff'),避免 UnicodeDecodeError;可手动判断或用 filetype 库简化识别。

用 open() 二进制模式读取无后缀文件
无后缀文件没法靠扩展名判断类型,只能看内容本身。最直接的办法就是以 rb 模式打开,跳过解码环节,原样拿到字节流——这是所有后续识别的前提。
常见错误是用 open(..., 'r') 尝试读取,结果遇到非 UTF-8 字节直接报 UnicodeDecodeError;或者没指定编码,让 Python 默认用系统 locale 解码,行为不可控。
- 必须用
open(path, 'rb'),不是'r' - 读多少?一般前 16–32 字节足够覆盖大多数 Magic Number(如 PNG 是前 8 字节,JPEG 是前 3 字节
b'\xff\xd8\xff') - 别用
readline(),它依赖换行符,在二进制里没意义;用read(n)精确控制字节数
常见 Magic Number 对应关系与 Python 判断逻辑
Magic Number 不是“猜”,而是查标准:PNG、JPEG、PDF、ELF、ZIP 等格式规范里都明确定义了开头若干字节的固定值。Python 里不需要自己硬背,用 if 链或字典匹配即可。
注意点:有些格式 Magic Number 有多个变体(比如 ZIP 文件开头可能是 b'PK\x03\x04' 或 b'PK\x05\x06'),也有格式 Magic Number 不在绝对开头(如某些 tar 文件需跳过 512 字节 header 才能定位到实际数据块)。
立即学习“Python免费学习笔记(深入)”;
让 macOS 自动化像说话一样简单 —— 用代码控制你的 Mac,无需学习 AppleScript 系统要求 macOS 14+ (Sonoma) Python 3.9+ 需要开启:辅助功能 + 屏幕录制权限
- PNG:
data.startswith(b'\x89PNG\r\n\x1a\n') - JPEG:
data.startswith(b'\xff\xd8\xff') - PDF:
data.startswith(b'%PDF-')(注意是 ASCII 文本,但仍在rb模式下可比对) - ZIP/EPUB:
data.startswith(b'PK\x03\x04') or data.startswith(b'PK\x05\x06') or data.startswith(b'PK\x07\x08') - ELF(Linux 可执行文件):
data.startswith(b'\x7fELF')
用 filetype 库省事但要注意边界
手动比对 Magic Number 写起来快,但维护麻烦。第三方库 filetype 就是干这事的,内部已预置上百种格式的 Magic 规则,调用简单:
import filetype
kind = filetype.guess('/path/to/file')
if kind is not None:
print(kind.mime, kind.extension)
但它不是万能的:不支持自定义 Magic 规则;对极小文件(None;某些嵌套格式(如 docx 本质是 ZIP,但 filetype 会优先识别为 application/vnd.openxmlformats-officedocument.wordprocessingml.document)返回的是高层语义类型,不是底层容器类型。
- 适合快速验证、脚本批量探查
- 不要在安全敏感场景(如上传文件类型校验)中只依赖它——攻击者可以构造头部合法但实际内容恶意的文件
- 它默认只读前 262 字节,如果某格式 Magic 在更后面(比如某些音频格式),会漏判
检测失败时该往哪查
如果按 Magic Number 匹配不到,先别急着怀疑代码,大概率是文件本身有问题,或你查的格式不在常规列表里。
这时候要回到原始字节,用十六进制视角看真实内容。Linux/macOS 下可用 xxd -l 32 /path/to/file,Python 里可以用 data[:32].hex() 或 data[:32].hex(' ') 辅助观察。
- 文件是否为空?
len(data) == 0要单独处理 - 是否被加密或混淆?比如某些打包工具会在真实 Magic 前插入一段 loader 字节
- 是否是自定义/私有格式?那就得找文档或样本反推 Magic 位置和长度
- 是否是文本文件但没后缀?比如 JSON、XML、YAML,它们没有传统 Magic Number,得靠内容特征(如开头是否有
{、、<code>---)做启发式判断,这已经超出字节头检测范畴了
真正难的从来不是“怎么读”,而是“读出来之后,你认得几个”。Magic Number 表是死的,文件是活的,多看几眼 xxd 输出,比背规则有用。

















