究竟是什么决定了文件的类型
Source: Dev.to
概览
每种文件格式都有一个规范——一种约定好的结构,定义了文件中字节的组织方式。就像我们有互联网协议的标准一样,也有文件类型的标准。当应用程序打开 PDF 或解析 PNG 时,它会按照该格式预先定义的规则读取字节。
我们通常通过文件扩展名(.zip、.txt、.jpg)来识别文件。扩展名仅仅是给人类和操作系统的提示;它们并不是唯一的真相。把 photo.jpg 重命名为 photo.png 并 不会 将图像转换为 PNG 格式。
文件类型的真正判定是通过 魔数(magic numbers)完成的。
魔数
魔数是位于文件开头或特定偏移位置的一串字节,用作唯一签名来识别文件格式或类型。
每种文件格式都有约定好的魔数,应用程序在需要确定文件格式时会检查这些魔数,而不管文件的扩展名是什么。
- PNG:
89 50 4E 47 - ZIP:
50 4B 03 04 - BMP:
42 4D(ASCII 字符 “BM”)
下面是一个 Go 示例,读取一个 .bmp 扩展名的文件并验证其魔数,以确认它是否真的是位图图像:
package main
import (
"bytes"
"fmt"
"io"
"log"
"os"
)
func main() {
f, err := os.Open("sample.bmp")
if err != nil {
log.Fatal(err)
}
defer f.Close()
// Read the first 2 bytes (the magic number)
header := make([]byte, 2)
if _, err := io.ReadFull(f, header); err != nil {
log.Fatal(err)
}
// BMP signature is 0x42, 0x4D
bmpSig := []byte{0x42, 0x4D}
if bytes.Equal(header, bmpSig) {
fmt.Println("Valid BMP detected")
} else {
fmt.Println("Invalid file format")
}
}
Unix 的 file 命令正是利用这些签名来识别文件,而不考虑其扩展名。签名之后,大多数格式都会包含描述内容的元数据(图像的尺寸、音频的采样率、文档的作者信息等)。
文件结构的分类
文件格式大致可以分为以下几类结构:
- 具有严格结构的二进制格式(PNG、JPEG、MP3):每个字节位置都有依据规范的特定含义。程序通过读取精确的偏移来解析这些文件。
- 基于文本的结构化格式(JSON、XML、HTML、CSV):可读的文本,遵循语法规则。调试更方便,但文件体积相对较大。
- 容器格式(ZIP、MP4、PDF):在单个文件内部充当文件系统,包含多个嵌入的文件或流。一个 MP4 可能包含独立的视频、音频和字幕轨道。DOCX 实际上是一个包含 XML 文件的 ZIP 包。
了解如何遍历字节并查阅规范,就能为许多文件类型编写自己的解析器。下一篇文章中,我将演示一个简单脚本,直接修改图像字节实现图像灰度化。