Ruby:为何unpack('Q')结果与手动uint64_t转换不同?
unpack('Q')的差异及输入长度无关的原因 这两个问题本质都是对二进制数据解析规则的理解偏差,我来给你逐个讲清楚:
1. 结果不一致:字节序(Endianness)在搞鬼
你手动转换和unpack('Q')的结果差异,核心是字节的解析顺序不同:
你的手动方法:
'abcdefgh'.unpack('B*').first会把每个字符的8位二进制按字符串顺序拼接,比如a(0x61 →01100001)、b(0x62 →01100010)……直到h(0x68 →01101000),最终得到的二进制串是0110000101100010...01101000,转成十六进制就是0x6162636465666768,对应十进制7017280452245743464——这相当于把字符串的字节按从左到右作为高位到低位(大端字节序)来解析。而Ruby的
unpack('Q')默认采用本机字节序,绝大多数现代电脑都是小端字节序:小端规则是把字符串的第一个字节作为整数的最低位,最后一个字节作为最高位。所以对于'abcdefgh'的字节序列[0x61, 0x62, ..., 0x68],解析出来的整数等价于把字节反转后按大端拼接,也就是十六进制0x6867666564636261,对应十进制7523094288207667809。
要让手动转换和unpack('Q')结果一致,只需要先反转字符串再处理:
Integer('abcdefgh'.reverse.unpack('B*').first, 2) # => 7523094288207667809
或者你也可以用unpack('Q>')明确指定大端字节序,这时结果就和你的手动转换匹配了:
'abcdefgh'.unpack('Q>').first # => 7017280452245743464
2. 输入长度不影响结果:unpack('Q')只取前8个字节
Q在unpack规则里代表64位无符号整数,而64位正好对应8个字节。所以不管你输入的字符串有多长,unpack('Q')只会读取字符串的前8个字节来解析,剩下的所有字节都会被直接忽略。
比如你输入'abcdefghijklmnop',它还是只处理前8个字符'abcdefgh',结果自然和之前一样。如果想解析字符串中所有的64位整数,可以用unpack('Q*'),它会按8字节一组依次解析整个字符串(长度不足8字节的部分会被忽略)。
内容的提问来源于stack exchange,提问作者Adelmar

