C++ 文件 IO:读取和写入 16 位字

Posted

技术标签:

【中文标题】C++ 文件 IO:读取和写入 16 位字【英文标题】:C++ File IO: Reading and Writing 16-bit Words 【发布时间】:2012-09-30 04:21:03 【问题描述】:

我想将非 Unicode 的 16 位字写入文件,稍后再读回。我知道通过一点字节操作,我可以在char 模式下使用fstream::read()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&lt;uint16_t&gt; 并得到了相同的结果。为什么?

【问题讨论】:

好吧,你读和写的数量不同。我不认为这就是你看到 Cs 的原因,但你必须在某个时候修复它 @Wug,这是一个剪切和粘贴错误。会修复的。 “类模板 basic_filebuf 将文件视为字节的源或接收器” -- §27.9.1[fstreams]/3 【参考方案1】:

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

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

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

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

【讨论】:

【参考方案2】:

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

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

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

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

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

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

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

【讨论】:

【参考方案3】:

我建议阅读: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,但它太新,无法移植。

【讨论】:

FWIW,char 可能是一个字节,但这并不意味着 8 位。例如TI 定点 DSP 有 16 位 char sizeof(int)=sizeof(short)=sizeof(char)=1 ) aw.write(reinterpret_cast(c), sizeof c); //? 这是真的,而且完全不重要,直到一些程序员忘记并假设他的平台字节是每个人的。看这个问题的读者可能会注意到limit.h(C++ 中的climit)定义了CHAR_BIT 中的# bytes per bit。【参考方案4】:

您可以使用 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 处理器)

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

【讨论】:

以上是关于C++ 文件 IO:读取和写入 16 位字的主要内容,如果未能解决你的问题,请参考以下文章

C++怎么读取或者写入到局域网共享的文件内容?

访问混合 8/16/32 位字的好方法

C++之文件的读取和写入操作(文本文件和二进制文件)

C++:从 txt 文件读取和写入的成员函数

在 C++ 中写入整数并从二进制文件中读取它们:字节数 mismaych

java读取excel文件内容,并将读取到的内容写入到另一文件中