跟 Windows 不同,在 Linux 中的文件扩展名仅仅是方便用户肉眼判断类型而已,将扩展名换掉并不会影响使用。
本质上,所有文件的内容都是以二进制保存的。程序之所以能将文件识别成不同的类型,无非就是不同类型的文件内容有区别于其他类型的特征。举个简单的例子,现在有一个系统,文件类型只有两种,那我们就可以将文件的第一个字节用于区分类型,将第一个字节为0000都视为同一类型的文件,第一个字节为0001的视为另一种文件。这个字节我们就称之为魔数,这种情况下,魔数用于区分文件类型。
现在通过 Linux 系统的file命令来看看这个魔数是怎么影响类型的。如下,创建一个名为helloworld.jpeg的文件,通过file命令知道这是一个 ASCII 编码的文本文件。
$ echo 'Hello world' > helloworld.jpeg
$ file helloworld.jpeg
helloworld.jpeg: ASCII text
接着,用vim -b命令以二进制模式打开文件,将在vim的命令模式下输入:%!xxd并回车。
$ vim -b helloworld.jpeg
Hello world
:%!xxd
以上操作之后,我们会看到现在的文件内容如下,这时需要谨慎操作。
00000000: 4865 6c6c 6f20 776f 726c 640a Hello world.
如下,将文件中的4865 6c6c...640a这些内容替换为以下的ffd8 ffe0...0001。注意,不要有多余的输入(包括空格)。
00000000: ffd8 ffe0 0010 4a46 4946 0001 0100 0001 Hello world.
然后,进入命令模式,输入命令:%!xxd -r并回车。
00000000: ffd8 ffe0 0010 4a46 4946 0001 0100 0001 Hello world.
:%!xxd -r
以上命令结束后,应该会看到内容又变了,如下。这时可以接着输入:wq命令保存退出了。
<ff><d8><ff><e0>^@^PJFIF^@^A^A^@^@^A
:wq
最后,再次用file看看文件类型,如无意外,应该跟下面的结果一样是 JPEG 图片格式。
$ file helloworld.jpeg
helloworld.jpeg: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x, segment length 16, thumbnail 0x
通过以上实验,可以发现file就是用了魔数来判断文件类型的,有些程序在用户上传文件时就可能会直接调用file命令辅助校验。
顺带说一下,所谓的字符编码,如 UTF-8,GBK 之类的,在识别具体编码时,原理也是相似的,不同的编码规则字节特征都不同。