将十六进制数据输出到文件的正确方法是什么?

时间:2011-06-12 16:45:57

标签: c++ hex fstream bmp

我读过有关[ostream] << hex << 0x[hex value]的内容,但我对此有一些疑问

(1)我使用output将我的文件流output.open("BWhite.bmp",ios::binary);定义为十六进制输出文件流,因为我这样做了,这会使hex参数成为output<<操作多余?

(2) 如果我有一个整数值,我想存储在文件中,我使用了这个:

int i = 0;
output << i;

我会以小端或大端存储吗?根据执行或编译程序的计算机,endi-ness是否会发生变化?

此值的大小是否取决于它运行的计算机?我需要使用hex参数吗?

(3)有没有办法将原始十六进制数字输出到文件?如果我希望文件具有十六进制数字43,我应该使用什么?

output << 0x43output << hex << 0x43都输出ASCII 4,然后输出ASCII 3。

输出这些十六进制数字的目的是制作.bmp文件的标题。

3 个答案:

答案 0 :(得分:7)

格式化的输出运算符<<仅用于:格式化输出。这是字符串。

因此,std::hex流操纵器告诉流将数字输出为格式为十六进制的字符串。

如果要输出原始二进制数据,请使用未格式化的输出函数 ,例如basic_ostream::putbasic_ostream::write

你可以像这样输出一个int:

int n = 42;
output.write(&n, sizeof(int));

此输出的字节顺序取决于体系结构。如果您希望获得更多控制权,我建议如下:

int32_t n = 42;
char data[4];
data[0] = static_cast<char>(n & 0xFF);
data[1] = static_cast<char>((n >> 8) & 0xFF);
data[2] = static_cast<char>((n >> 16) & 0xFF);
data[3] = static_cast<char>((n >> 24) & 0xFF);
output.write(data, 4);

此示例将输出32位整数作为little-endian,而不管平台的字节顺序如何。但是,如果char已签名,请小心转换回去。

答案 1 :(得分:7)

你说

  

“有没有办法将原始十六进制数字输出到文件?如果我希望文件具有十六进制数字43,我应该使用什么?”

“原始十六进制数字”取决于您对一组位的解释。请考虑以下事项:

 Binary  :    0 1 0 0 1 0 1 0
 Hex     :    4 A
 Octal   :    1 1 2
 Decimal :    7 4
 ASCII   :    J

以上所有代表相同的数字量,但我们对它的解释不同。

因此,您只需将数据存储为二进制格式,即由数字表示的精确位模式。

<强> EDIT1

当您以文本模式打开文件并在其中写入数字时,请说当您编写74时(如上例所示),它将存储为两个ASCII字符'7'和{{1 }。为避免这种情况,请以二进制模式'4'打开文件,并使用ios::binary进行编写。查看http://courses.cs.vt.edu/~cs2604/fall00/binio.html#write

答案 2 :(得分:3)

  

输出这些十六进制数字的目的是制作.bmp文件的标题。

您似乎对文件的工作方式存在很大的误解

流操作符<<生成文本(人类可读输出)。 .bmp文件格式是一种二进制格式,不是人类可读的(它会不会很好,我不会在没有工具的情况下阅读它。)

您真正想要做的是生成二进制输出并将其放在文件中:

char   x = 0x43;
output.write(&x, sizeof(x));

这会将十六进制值0x43的一个字节数据写入输出流。这是您想要的二进制表示。

  

我会以小端或大端存储吗?根据执行或编译程序的计算机,endi-ness是否会发生变化?

既不;你又在输出文字(不是二进制数据)。

int i = 0;
output.write(reinterpret_cast<char*>(&i), sizeof(i)); // Writes the binary representation of i

在这里,您需要担心整数值的endianess(和大小),这将根据您运行应用程序的硬件而有所不同。对于值0,没有太多关于endianess的担心,但你应该担心整数的大小。

我会在代码中添加一些断言来验证代码的架构是否正常。然后让人们担心他们的架构是否符合要求:

int test = 0x12345678;
assert((sizeof(test) * CHAR_BITS == 32) && "BMP uses 32 byte ints");
assert((((char*)&test)[0] == 0x78) && "BMP uses little endian");

有一系列功能可以帮助您获得结束和大小。

http://www.gnu.org/s/hello/manual/libc/Byte-Order.html

  

功能:uint32_t htonl(uint32_t hostlong)
      此函数将uint32_t整数hostlong从主机字节顺序转换为网络字节顺序。

// Writing to a file
uint32_t hostValue = 0x12345678;
uint32_t network   = htonl(hostValue);
output.write(&network, sizeof(network));

// Reading from a file
uint32_t network;
output.read(&network, sizeof(network);
uint32_t hostValue = ntohl(network);    // convert back to platform specific value.

// Unfortunately the BMP was written with intel in-mind
// and thus all integers are in liitle-endian.
// network bye order (as used by htonl() and family) is big endian.
// So this may not be much us to you.

最后一件事。当您以二进制格式output.open("BWhite.bmp",ios::binary)打开文件时,除了处理end of line sequence的方式之外,它不会产生任何影响。当文件是二进制格式时,输出不会被修改(您在流中放入的内容是写入文件的内容)。如果将流保留为文本模式,则'\ n'字符将转换为行结束序列(特定于操作系统的字符串,用于定义行尾)。由于您正在编写二进制文件,因此您绝对不希望对所写的字符产生任何干扰,因此二进制文件的格式正确。但它不会影响您在流上执行的任何其他操作。