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<uint16_t>
并得到了相同的结果。为什么?
【问题讨论】:
好吧,你读和写的数量不同。我不认为这就是你看到 Cs 的原因,但你必须在某个时候修复它 @Wug,这是一个剪切和粘贴错误。会修复的。 “类模板basic_filebuf
将文件视为字节的源或接收器” -- §27.9.1[fstreams]/3
【参考方案1】:
我真的很惊讶您将流实例化以进行任何读取!结果可能是实现定义的(即,您可能会发现编译器文档中描述的行为)但可能只是没有指定(尽管不是完全未定义)。我认为流类不需要立即支持 char
和 wchar_t
以外的其他类型的实例化,即,无需用户提供至少一些方面。
标准流类是字符类型的模板,但对于任何不受支持的类型都不容易实例化。至少,您需要实现一个合适的std::codecvt<int16_t, char, std::mbstate_t>
facet 在字节的外部表示和内部表示之间进行转换。从外观上看,您尝试的两个系统对其默认实现做出了不同的选择。
std::codecvt<internT, externT, stateT>
是用于在字符的外部表示和字符的内部表示之间进行转换的构面。流只需要支持char
,它被认为将字节表示为外部类型externT
。内部字符类型internT
可以是任何整数类型,但转换需要通过实现代码转换方面来定义。如果我没记错的话,流也可以假设状态类型stateT
是std::mbstate_t
(这实际上有点问题,因为没有为这种类型定义接口!)。
除非您真的致力于为您的字符类型uint16_t
创建 I/O 流,否则您可能希望使用 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>
;我认为它们的迭代器类型适合默认)。
【讨论】:
【参考方案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_castCHAR_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 位字的主要内容,如果未能解决你的问题,请参考以下文章