我创建了一个文件(在 vim 中),用于测试目的(在 SSH 客户端中测试 UTF-8 输出)。然而,这个文件发生了奇怪的事情。
我想知道文件中有哪些字节,所以我使用了hexdump
:
username@computername:~$ hexdump -x intl.txt
0000000 9ecf 000a
0000003
好吧,那里有四个字节,00 和 0a 是如何进入那里的,我不清楚,但无论如何。不过,这就是奇怪的地方:
username@computername:~$ ls -al intl.txt
-rw-rw-r-- 1 username username 3 Mar 26 15:14 intl.txt
等等,是三个字节?这里发生了什么?
好像这还不够奇怪,hexdump -C
给出了非常不同的输出:
username@computername:~$ hexdump -C intl.txt
00000000 cf 9e 0a |...|
00000003
Vim 对这个文件也有点困惑。当我启动它时,它会在状态行中给出以下内容:
"intl.txt" 1L, 3C
然而,在顶部,我得到了这个(使用set list
):
Ϟ$
~
~
~
~
因此,它认为有 3 个字符,但只打印了 1 个。如果它打印了 koppa 和它下面的空行,我可以理解......
答案1
正如其他人所指出的,这是因为hexdump -x
将文件视为包含 2 字节字。在小端系统(几乎所有桌面都是),这意味着字节在显示之前将被交换。这意味着字节值是成对打印的,并且这些字节的顺序被交换。由于字节数为奇数,因此hexdump
只需添加一个零即可组成最终的一对。然后将零与 交换0a
。这是有记录的行为hexdump
,所以它不是在骗你!
使用hexdump -C
是一个更好的命令来获取格式化输出,该输出按字节在文件中的顺序显示字节。另外,这0a
是一个新行,可能是由创建文件的任何内容悄悄添加的(vim
默认情况下这样做)。例如,echo
如果您不告诉它不要这样做,则始终会添加新行。在bash
:
echo -e '\xcf\x9e' | hexdump -C
将给出相同的结果,但抑制换行符-n
将给出您所期望的结果:
echo -ne '\xcf\x9e' | hexdump -C
要停止vim
添加换行符:
:set noeol
:set binary
答案2
hexdump -x
将值显示为 2 字节整数。上一个小尾数法机器这将以交换的顺序显示每对字节,将它们视为双字节数量,首先是高位(第二个)字节,然后是低位(第一个)字节。
如您所见,使用hexdump -C
显示实际字节。文件的实际内容是两个字节 0xCF 0x9E,后跟换行符 0x0A。 Vim
并且ls
正确地告诉您有 3 个字节(2 个字符)。前两个字节包含一个使用 UTF-8 编码的 Unicode 字符。
更多有趣的信息在上面的评论中。
答案3
如果您无法理解字节顺序,请参阅另一个示例。
#include <stdio.h>
#include <inttypes.h>
#include <unistd.h>
int main (void) {
uint16_t x = 1;
write(1, &x, 2);
x = 2;
write(1, &x, 2);
return 0;
}
这是 C 代码,它将写出 2 个 16 位值 1 和 2。当我们考虑值时,我们将它们视为大端,因此这里的填充(以生成这些 16 位值)意味着您有一个零字节然后是一个值 1(或 2)的字节。然而,由于该系统是小端这里考虑这两个离散的 16 位(2 字节)单元,实际写出的四个字节是 1、0、2、0。
如果您编译该 ( gcc whatever.c
) 并重定向到文件 ( ./a.out > dword
),hexdump -C
将显示字节的物理顺序:
> hexdump -C dword
00000000 01 00 02 00 |....|
00000004
但在这种情况下,hexdump -x
将在含义方面提供更正确的解释,因为它交换字节以显示正确的两个 16 位值:
> hexdump -x dword
0000000 0001 0002
0000004
如果这四个字节被解释为(小端)32 位整数:
> hexdump -e '"%d\n"' dword
131073
因为它正在将以下 32 位二进制值转换为十进制值:
00000001 00000000 00000010 00000000
作为一个大尾数法值,即 2^9 (512) + 2^24 (16777216)。这就是我所说的我们以大字节顺序“思考”的意思。如果我们写出一个二进制数,我们使用大尾数法位顺序(一个字节00000010
== 2)所以当数字长于一个字节时,我们将使用大端字节顺序(两个字节0000000000000010
== 2)。
但由于系统是小端序,1如果我们想将这些字节写为二进制数,填充到 32 个位置(为了可读性,每 8 位使用相同的空格),我们将有:
00000000 00000010 00000000 00000001
以十进制表示,2^17 (131072) + 2^0 (1)。事实上,如果将程序主体替换为:
int main (void) {
uint32_t x = 131073;
write(1, &x, 4);
return 0;
}
编译并写入文件,你会得到完全相同的输出与hexdump
以前一样,因为该文件包含完全相同的内容。
1. 请注意,当我们谈论字节顺序时,它实际上总是指字节顺序。由于最小单位实际上是字节,因此其位顺序无关紧要。