3

我想将非 Unicode 的 16 位字写入文件,稍后再读回。我知道通过一些字节操作我可以在char模式下使用fstream::read()and来做到这一点fstream::write()。我需要做什么才能直接使用 16 位字?

例如,似乎我应该能够执行以下操作:

 basic_ofstream<uint16_t> aw;
 aw.open("test.bin", ios::binary);
 uint16_t c[] = {0x55aa, 0x1188};
 aw.write(c, 2);
 aw.close();

 basic_ifstream<uint16_t> ax;
 ax.open("test.bin", ios::binary);
 uint16_t ui[2];
 ax.read(ui, 2);
 ax.close();
 cout << endl << hex << unsigned(ui[0]) << " " << unsigned(ui[1]) << endl;

gcc 4.4 输出:

d 0

VC++10 输出:

CCCC CCCC

我也尝试过使用std::basic_filebuf<uint16_t>直接并得到相同的结果。为什么?

4

4 回答 4

6

我真的很惊讶您将流实例化以进行任何阅读!结果可能是实现定义的(即,您可能会发现编译器文档中描述的行为)但可能只是没有指定(尽管不是完全未定义)。我认为流类不需要立即支持其他类型的实例化charwchar_t即不需要用户提供至少一些方面。

标准流类是字符类型的模板,但对于任何不受支持的类型都不容易实例化。至少,您需要std::codecvt<int16_t, char, std::mbstate_t>在以字节为单位的外部表示和内部表示之间实现合适的方面转换。从外观上看,您尝试的两个系统对其默认实现做出了不同的选择。

std::codecvt<internT, externT, stateT>是用于在字符的外部表示和字符的内部表示之间进行转换的方面。Streams 只需要支持char将字节表示为外部类型externT。内部字符类型internT可以是任何整数类型,但需要通过实现代码转换方面来定义转换。如果我没记错的话,流也可以假设状态类型stateTstd::mbstate_t(这实际上有点问题,因为没有为这种类型定义接口!)。

除非您真的致力于为您的字符类型创建 I/O 流,否则您uint16_t可能希望使用读取字节std::ifstream并将它们转换为您的字符类型。写字符也是如此。要真正创建一个也支持格式化的 I/O 流,您还需要许多其他方面(例如 , std::ctype<uint16_t>std::num_punct<uint16_t>,并且您需要构建一个std::locale来包含所有这些以及一些可以从标准库的实现(例如,std::num_get<uint16_t>std::num_put<uint16_t>;我认为它们的迭代器类型是合适的默认值)。

于 2012-10-09T19:30:09.807 回答
2

当我尝试您的代码时,文件已写入,但里面没有任何内容,关闭后其大小为 0。从该文件读取时,无法读取任何内容。您在输出中看到的是未初始化的垃圾。

除了使用带有默认字符的 ofstream/ifstream 之外,您不一定要依赖read()andwrite()方法,因为它们并不表示它们是否真的写了任何东西。有关详细信息,请参阅http://en.cppreference.com/w/cpp/io/basic_ostream/write。尤其是这很有趣:

这个函数是一个未格式化的输出函数:它通过构造一个哨兵类型的对象开始执行,如果需要,它会刷新 tie() 的输出缓冲区并检查流错误。构造完成后,如果哨兵对象返回 false,则函数返回而不尝试任何输出。

很可能这就是为什么没有将输出写入您的文件的原因,因为它似乎不是为使用除 char 或类似类型之外的任何其他类型而设计的。

更新:要查看写入/读取是否成功,请检查应该已经表明出现问题的失败或坏位。

cout << aw.fail() << aw.bad() << "\n";
cout << ax.fail() << ax.bad() << "\n";

两者都设置为 true,所以你真正的问题应该是:为什么调用 write()失败?

于 2012-10-09T19:48:30.403 回答
1

我建议阅读:http ://www.cplusplus.com/articles/DzywvCM9/

片段:

“这些类型的问题在于它们的大小没有明确定义。 int 在一台机器上可能是 8 个字节,但在另一台机器上只有 4 个字节。唯一一致的是 char ......它保证始终是 1 个字节。 "

u16 ReadU16(istream& file)
{
  u16 val;
  u8 bytes[2];

  file.read( (char*)bytes, 2 );  // read 2 bytes from the file
  val = bytes[0] | (bytes[1] << 8);  // construct the 16-bit value from those bytes

  return val;
}

void WriteU16(ostream& file, u16 val)
{
  u8 bytes[2];

  // extract the individual bytes from our value
  bytes[0] = (val) & 0xFF;  // low byte
  bytes[1] = (val >> 8) & 0xFF;  // high byte

  // write those bytes to the file
  file.write( (char*)bytes, 2 );
}

您可能还想刷新“typedef”关键字,以定义保证的-#-bits 类型。虽然学习曲线多一点,但 Boost 和 C99 编译器也定义了有保证的大小类型。我不确定 X++0x,但它太新而不能移植。

于 2012-10-09T19:27:54.947 回答
1

您可以使用 char 特化和 reinterpret_cast:

basic_ofstream<char> aw;
...
aw.write( reinterpret_cast<const char*>(i16buf), n2write*sizeof(int16_t) );

basic_ifstream<char> ax;
...
ax.read( reinterpret_cast<char*>(i16buf), n2read*sizeof(int16_t) );

“sizeof(int16_t)”适用于 sizeof(int16_t)==1 的边缘情况(例如 DSP 处理器)

当然,如果你需要以特定的字节顺序读/写,那么你需要字节序转换函数。请注意,没有确定字节顺序的标准编译时方法。

于 2012-10-09T19:49:26.247 回答