读取小端序16位无符号整数

3
我正在研究解析terminfo数据库文件,这是一种二进制文件类型。您可以自己阅读有关存储格式的内容并确认我面临的问题。
手册上说-
部分开始文件。此部分包含以下格式的六个短整数。这些整数是:

(1) 魔术数字(八进制0432);

...

...

短整数存储在两个8位字节中。第一个字节包含值的最低有效8位,第二个字节包含最高有效8位。(因此,表示的值为256*second+first)。值-1由两个字节0377, 0377表示;其他负值是非法的。这个值通常意味着此终端缺少相应的功能。 这与硬件不对应的机器必须将整数读取为两个字节并计算小端值


  • 解析这种类型的输入时,第一个问题是它将大小固定为8位,因此无法使用普通的char,因为它不能保证大小恰好为8位。所以我看了一下 '固定宽度整数类型' 但又面临着选择int8_t或uint8_t之间的困境,它明确说明 - "仅在实现直接支持该类型时提供"。那么我应该选择什么样的类型才能够足够便携呢。

  • 第二个问题是,在c++标准库中没有buffer.readInt16LE()方法,该方法可以读取小端格式的16字节数据。那么我该如何继续以可移植和安全的方式实现此函数。

我已经尝试使用char数据类型进行读取,但它在我的机器上肯定会产生垃圾。可以通过infocmp命令读取正确的输入,例如 - $ infocmp xterm


#include <fstream>
#include <iostream>
#include <vector>

int main()
{
    std::ifstream db(
      "/usr/share/terminfo/g/gnome", std::ios::binary | std::ios::ate);

    std::vector<unsigned char> buffer;

    if (db) {
        auto size = db.tellg();
        buffer.resize(size);
        db.seekg(0, std::ios::beg);
        db.read(reinterpret_cast<char*>(&buffer.front()), size);
    }
    std::cout << "\n";
}

$1 = std::vector of length 3069, capacity 3069 = {26 '\032', 1 '\001', 21 '\025',
  0 '\000', 38 '&', 0 '\000', 16 '\020', 0 '\000', 157 '\235', 1 '\001',
  193 '\301', 4 '\004', 103 'g', 110 'n', 111 'o', 109 'm', 101 'e', 124 '|',
  71 'G', 78 'N', 79 'O', 77 'M', 69 'E', 32 ' ', 84 'T', 101 'e', 114 'r',
  109 'm', 105 'i', 110 'n', 97 'a', 108 'l', 0 '\000', 0 '\000', 1 '\001',
  0 '\000', 0 '\000', 1 '\001', 0 '\000', 0 '\000', 0 '\000', 0 '\000',
  0 '\000', 0 '\000', 0 '\000', 0 '\000', 1 '\001', 1 '\001', 0 '\000',
....
....

1
请发一些代码,我的朋友。尝试将一些字节读入缓冲区,并使用调试器查看它。 - john elemans
1
“char”在任何系统上都保证是可寻址的最小单元(这就是为什么“sizeof(char)”始终指定为“1”的原因)。因此,在一个具有8位字节的系统中,“char”保证是8位。而且,由于实际上几乎所有的系统都是在过去30年左右制造的,所以真的没有必要担心。如果您需要将程序移植到一些1970年代(或更早)的老系统上,那么您可能需要担心它,但否则不需要。 - Some programmer dude
1
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Thomas Matthews
1
  1. 添加一个断言:CHAR_BIT==8
  2. 不要陷入“字节顺序谬论”的陷阱中(https://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html)。
- wally
1
正如一个回答所说,如果你的代码使用 char 无法工作,那么你的代码就是有问题的。 - Carey Gregory
显示剩余2条评论
1个回答

2
解析这种类型的输入时的第一个问题是,它将其大小固定为8位,因此不能使用普通字符(char)类型,因为它无法保证大小恰好为8位。
任何至少有8位的整数都可以。虽然 char 类型不能保证恰好为8位,但它至少需要是8位,因此就大小而言,没有问题,除非在某些情况下需要屏蔽高位(如果存在)。然而,char 可能不是无符号的,并且您不希望将八位元解释为有符号值,因此请改用 unsigned char。
第二个问题是,在 C++ 标准库中没有 buffer.readInt16LE() 方法可以读取小端格式的 16 字节数据。那么,我应该如何以一种可移植且安全的方式实现这个函数呢?
逐个字节地读入一个 unsigned char。将第一个字节分配给变量(足够大,至少可以表示16位)。使用复合位或运算将第二个字节的位向左移8位并分配给变量。
或者更好的方法是,不要重新实现它,而是使用已经存在的第三方库。
我已经尝试使用 char 数据类型读取它,但在我的机器上它肯定会产生垃圾。
那么你的尝试有错误。 char 没有固有的问题会导致输出垃圾。我建议使用调试器来解决这个问题。

我已经添加了代码和示例输出,您能告诉我哪里出了问题吗? - Abhinav Gauniyal
嗯,我尝试定义了两个 unit8_t 变量 xy,并使用 db.read(reinterpret_cast<char*>(&x), sizeof(x)); 读取数据,然后按照你在另一个 unit16_t 中建议的方法进行操作 - result = x | (y << 8); 结果是 282,这确实是八进制中的 0432。不太确定为什么调试器输出会是那样。所以这种方法适用于小端和大端机器吗? - Abhinav Gauniyal
是的,这将小端转换为本地端,无论本地端是什么。 - eerorika
或者更好的方法是不要重新实现它,而是使用现有的库。哪个库?有标准库吗? - BarbaraKwarc
1
@BarbaraKwarc 我的意思是特指第三方库。在C++标准库中没有这方面的函数,但在POSIX标准C库中有。Boost也有非常好的工具集可供使用。 - eerorika

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接