为什么 std::basic_fstream<unsigned char> 不起作用?

Why std::basic_fstream<unsigned char> won't work?

尝试编译此代码时:

std::fstream file("file.name", std::ios::out | std::ios::binary);
uint8_t buf[BUFSIZE];
//Fill the buffer, etc...
file.write(buf, BUFSIZE);

编译器会警告我在调用 write() 时从 unsigned charchar 的非常不健康的转换。由于 std::fstream 实际上只是 std::basic_fstream<char> 的类型定义,有人可能会认为使用 std::basic_fstream<uint8_t> 可以让他们在没有警告的情况下编译上面的代码,因为 write() 需要模板的指针类型。

这当然有效,但又出现了另一个问题。即使这段代码编译得很好:

std::basic_fstream<uint8_t> file("file.name", std::ios::out | std::ios::binary);
uint8_t buf[BUFSIZE];
//Fill the buffer, etc...
file.write(buf, BUFSIZE);

它现在将在调用 write() 时失败,即使以前的版本可以正常工作(忽略编译器警告)。我花了一段时间才查明标准 C++ 库代码中抛出异常的位置,但我仍然不太明白这是怎么回事。看起来 std::basic_fstream 使用了一些字符编码机制,并且由于为 char 定义了一个但 none 为 unsigned char 定义了一个,因此文件流在尝试使用时静默失败“错误的”字符数据类型...至少我是这样看的。

但这也是我不明白的。不需要任何字符编码。我什至不以文本模式打开文件,我想处理二进制数据。这就是为什么我使用 uint8_t 类型的数组,而不是 char,使用这种数据类型比使用普通的 char 感觉更自然。但在我决定放弃 uint8_t 数据类型并接受使用 char 缓冲区之前,或者开始使用定义为 char 的自定义 byte 数据类型数组之前,我'我想问两个问题:

  1. 阻止我使用无符号字符数据类型的机制到底是什么?它真的与字符编码有关,还是有其他用途?为什么文件流适用于带符号的字符数据类型,但不适用于无符号的数据类型?
  2. 假设我仍然想使用 std::basic_fstream<uint8_t>,无论它多么(不)合理 - 有什么办法可以实现吗?

std::basic_fstream<unsigned char> 不起作用,因为它使用 std::char_traits<unsigned char>,但标准库不提供这样的专业化,请参阅 std::char_traits 了解详细信息。

如果你想read/write二进制数据,你需要使用std::basic_fstream<char>,用std::ios_base::binary标志打开它并使用std::basic_ostream<CharT,Traits>::write函数写入二进制数据.

这是一些遗留问题,因为所有 char 类型都可用于表示二进制数据。标准库使用 char 可能是因为这是完成这项工作的最短的输入和阅读方式。


What exactly is that mechanism that stops me from using unsigned character datatype?

没有 std::char_traits<unsigned char> 专业化。

Is it really something related to character encoding, or does it serve some other purpose?

std::char_traits 在其界面中有一些明确定义的用途,但不包括 decoding/encoding。后者由 codecvt 完成,请参阅那里的用法示例。

Why file stream works fine with signed character data types, but not for unsigned ones?

因为std::basic_ostream<CharT,Traits>::write 接受CharT,您为流指定的第一个模板参数。它写入与读取相同的字符类型,并使用 codecvtCharT 转换为字节。

Assuming that I still would want to use std::basic_fstream<uint8_t>, regardless how (un)reasonable it is - is there any way to achieve that?

标准 class 和函数模板不能专门用于内置类型 if I am not mistaken。您需要使用 std::char_traits 接口创建另一个 class 并将其指定为标准流的第二个模板参数。我想,你需要一个非常强大的(哲学上的)理由才能卷起袖子去做。

如果不这样做,您可能想继续使用 std::fstream<char> 并执行 stream.write(reinterpret_cast<char const*>(buf), sizeof buf);

其实charuint8_t可以是不同的类型。这也意味着它们可以有不同的 std::char_traits。字符特征类型是 std::basic_fstream 的第二个模板参数,默认情况下是 std::char_traits 用字符类型实例化的。 std::basic_fstream 默认通过字符特征模板参数格式化 I/O。它不简单地重定向原始字节不变。这可能就是您得到不同结果的原因。