C++ 如何将大端结构转换为小端结构?
我有一个在unix机器上创建的二进制文件。这只是一堆一个接一个的记录。记录的定义如下:C++ 如何将大端结构转换为小端结构?,c++,struct,endianness,C++,Struct,Endianness,我有一个在unix机器上创建的二进制文件。这只是一堆一个接一个的记录。记录的定义如下: struct RECORD { UINT32 foo; UINT32 bar; CHAR fooword[11]; CHAR barword[11]; UNIT16 baz; } fstream f; f.open("file.bin", ios::in | ios::binary); RECORD r; f.read((char*)&detail, sizeof(RECOR
struct RECORD {
UINT32 foo;
UINT32 bar;
CHAR fooword[11];
CHAR barword[11];
UNIT16 baz;
}
fstream f;
f.open("file.bin", ios::in | ios::binary);
RECORD r;
f.read((char*)&detail, sizeof(RECORD));
cout << "fooword = " << r.fooword << endl;
inline u_int ByteSwap(u_int in)
{
u_int out;
char *indata = (char *)∈
char *outdata = (char *)&out;
outdata[0] = indata[3] ;
outdata[3] = indata[0] ;
outdata[1] = indata[2] ;
outdata[2] = indata[1] ;
return out;
}
inline u_short ByteSwap(u_short in)
{
u_short out;
char *indata = (char *)∈
char *outdata = (char *)&out;
outdata[0] = indata[1] ;
outdata[1] = indata[0] ;
return out;
}
struct RECORD {
UINT32 foo;
UINT32 bar;
CHAR fooword[11];
CHAR barword[11];
UNIT16 baz;
void SwapBytes()
{
foo = ByteSwap(foo);
bar = ByteSwap(bar);
baz = ByteSwap(baz);
}
}
fstream f;
f.open("file.bin", ios::in | ios::binary);
RECORD r;
f.read((char*)&detail, sizeof(RECORD));
r.SwapBytes();
cout << "fooword = " << r.fooword << endl;
uint32 parse_uint32(char* buffer)
{
uint32 x;
// ...
return x;
}
uint32 value = *reinterpret_cast<uint32*>)(ptr); ptr += 4;
return value;
我正试图弄清楚如何在Windows机器上读取和解释这些数据。我有这样的想法:
struct RECORD {
UINT32 foo;
UINT32 bar;
CHAR fooword[11];
CHAR barword[11];
UNIT16 baz;
}
fstream f;
f.open("file.bin", ios::in | ios::binary);
RECORD r;
f.read((char*)&detail, sizeof(RECORD));
cout << "fooword = " << r.fooword << endl;
inline u_int ByteSwap(u_int in)
{
u_int out;
char *indata = (char *)∈
char *outdata = (char *)&out;
outdata[0] = indata[3] ;
outdata[3] = indata[0] ;
outdata[1] = indata[2] ;
outdata[2] = indata[1] ;
return out;
}
inline u_short ByteSwap(u_short in)
{
u_short out;
char *indata = (char *)∈
char *outdata = (char *)&out;
outdata[0] = indata[1] ;
outdata[1] = indata[0] ;
return out;
}
struct RECORD {
UINT32 foo;
UINT32 bar;
CHAR fooword[11];
CHAR barword[11];
UNIT16 baz;
void SwapBytes()
{
foo = ByteSwap(foo);
bar = ByteSwap(bar);
baz = ByteSwap(baz);
}
}
fstream f;
f.open("file.bin", ios::in | ios::binary);
RECORD r;
f.read((char*)&detail, sizeof(RECORD));
r.SwapBytes();
cout << "fooword = " << r.fooword << endl;
uint32 parse_uint32(char* buffer)
{
uint32 x;
// ...
return x;
}
uint32 value = *reinterpret_cast<uint32*>)(ptr); ptr += 4;
return value;
fsf;
f、 打开(“file.bin”,ios::in | ios::binary);
记录r;
f、 读取((字符*)和详细信息,大小(记录));
cout它独立地影响每个成员,而不是整个struct
。而且,它不会影响数组之类的东西。例如,它只是将int
s中的字节按相反顺序存储
也就是说,可能有一台机器有着奇怪的末端。我刚才所说的适用于大多数使用过的机器(x86、ARM、PowerPC、SPARC)。实际上,端性是底层硬件的属性,而不是操作系统的属性
最好的解决方案是在编写数据时转换为标准——谷歌搜索“网络字节顺序”,您应该找到实现这一点的方法
编辑:这里有一个链接:您必须分别更正多个字节的每个成员的尾数。字符串不需要转换(fooword和barword),因为它们可以被视为字节序列
但是,您必须注意另一个问题:结构中成员的aligmenent。基本上,您必须检查unix和windows代码上的sizeof(RECORD)是否相同。编译器通常提供pragma来定义所需的aligment(例如,#pragma pack)。以及endian,您需要注意两个平台之间的填充差异。特别是如果您有奇数长度字符数组和16位值,您可能会发现某些元素之间的pad字节数不同
编辑:如果结构是在没有包装的情况下写出来的,那么它应该是相当简单的。类似这样(未经测试)的代码应该可以完成这项工作:
// Functions to swap the endian of 16 and 32 bit values
inline void SwapEndian(UINT16 &val)
{
val = (val<<8) | (val>>8);
}
inline void SwapEndian(UINT32 &val)
{
val = (val<<24) | ((val<<8) & 0x00ff0000) |
((val>>8) & 0x0000ff00) | (val>>24);
}
<>你还必须考虑这两个编译器之间的对齐差异。允许每个编译器在最适合体系结构的结构中的成员之间插入填充。所以你真的需要知道:
- UNIX程序如何写入文件
- 如果它是对象的二进制副本,则表示结构的确切布局
- 如果它是一个二进制副本,那么源架构的endian属性是什么
这就是为什么大多数程序(我见过(需要是平台无关的))将数据序列化为一个文本流,可以很容易地被标准iostreams读取。类似的方法应该可以工作:
#include <algorithm>
struct RECORD {
UINT32 foo;
UINT32 bar;
CHAR fooword[11];
CHAR barword[11];
UINT16 baz;
}
void ReverseBytes( void *start, int size )
{
char *beg = start;
char *end = beg + size;
std::reverse( beg, end );
}
int main() {
fstream f;
f.open( "file.bin", ios::in | ios::binary );
// for each entry {
RECORD r;
f.read( (char *)&r, sizeof( RECORD ) );
ReverseBytes( r.foo, sizeof( UINT32 ) );
ReverseBytes( r.bar, sizeof( UINT32 ) );
ReverseBytes( r.baz, sizeof( UINT16 )
// }
return 0;
}
#包括
结构记录{
UINT32-foo;
UINT32巴;
字[11];
字符条码[11];
UINT16-baz;
}
无效反向字节(无效*开始,整数大小)
{
char*beg=start;
char*end=beg+size;
标准::反向(beg,end);
}
int main(){
fsf;
f、 打开(“file.bin”,ios::in | ios::binary);
//对于每个条目{
记录r;
f、 读取((字符*)&r,大小(记录));
反向字节(r.foo,sizeof(UINT32));
反向字节(r.bar,sizeof(UINT32));
反向字节(r.baz,尺寸为UINT16)
// }
返回0;
}
我喜欢为每个需要交换的数据类型实现一个交换字节方法,如下所示:
struct RECORD {
UINT32 foo;
UINT32 bar;
CHAR fooword[11];
CHAR barword[11];
UNIT16 baz;
}
fstream f;
f.open("file.bin", ios::in | ios::binary);
RECORD r;
f.read((char*)&detail, sizeof(RECORD));
cout << "fooword = " << r.fooword << endl;
inline u_int ByteSwap(u_int in)
{
u_int out;
char *indata = (char *)∈
char *outdata = (char *)&out;
outdata[0] = indata[3] ;
outdata[3] = indata[0] ;
outdata[1] = indata[2] ;
outdata[2] = indata[1] ;
return out;
}
inline u_short ByteSwap(u_short in)
{
u_short out;
char *indata = (char *)∈
char *outdata = (char *)&out;
outdata[0] = indata[1] ;
outdata[1] = indata[0] ;
return out;
}
struct RECORD {
UINT32 foo;
UINT32 bar;
CHAR fooword[11];
CHAR barword[11];
UNIT16 baz;
void SwapBytes()
{
foo = ByteSwap(foo);
bar = ByteSwap(bar);
baz = ByteSwap(baz);
}
}
fstream f;
f.open("file.bin", ios::in | ios::binary);
RECORD r;
f.read((char*)&detail, sizeof(RECORD));
r.SwapBytes();
cout << "fooword = " << r.fooword << endl;
uint32 parse_uint32(char* buffer)
{
uint32 x;
// ...
return x;
}
uint32 value = *reinterpret_cast<uint32*>)(ptr); ptr += 4;
return value;
然后我向需要交换的结构中添加一个函数,如下所示:
struct RECORD {
UINT32 foo;
UINT32 bar;
CHAR fooword[11];
CHAR barword[11];
UNIT16 baz;
}
fstream f;
f.open("file.bin", ios::in | ios::binary);
RECORD r;
f.read((char*)&detail, sizeof(RECORD));
cout << "fooword = " << r.fooword << endl;
inline u_int ByteSwap(u_int in)
{
u_int out;
char *indata = (char *)∈
char *outdata = (char *)&out;
outdata[0] = indata[3] ;
outdata[3] = indata[0] ;
outdata[1] = indata[2] ;
outdata[2] = indata[1] ;
return out;
}
inline u_short ByteSwap(u_short in)
{
u_short out;
char *indata = (char *)∈
char *outdata = (char *)&out;
outdata[0] = indata[1] ;
outdata[1] = indata[0] ;
return out;
}
struct RECORD {
UINT32 foo;
UINT32 bar;
CHAR fooword[11];
CHAR barword[11];
UNIT16 baz;
void SwapBytes()
{
foo = ByteSwap(foo);
bar = ByteSwap(bar);
baz = ByteSwap(baz);
}
}
fstream f;
f.open("file.bin", ios::in | ios::binary);
RECORD r;
f.read((char*)&detail, sizeof(RECORD));
r.SwapBytes();
cout << "fooword = " << r.fooword << endl;
uint32 parse_uint32(char* buffer)
{
uint32 x;
// ...
return x;
}
uint32 value = *reinterpret_cast<uint32*>)(ptr); ptr += 4;
return value;
然后,您可以修改读取(或写入)结构的代码,如下所示:
struct RECORD {
UINT32 foo;
UINT32 bar;
CHAR fooword[11];
CHAR barword[11];
UNIT16 baz;
}
fstream f;
f.open("file.bin", ios::in | ios::binary);
RECORD r;
f.read((char*)&detail, sizeof(RECORD));
cout << "fooword = " << r.fooword << endl;
inline u_int ByteSwap(u_int in)
{
u_int out;
char *indata = (char *)∈
char *outdata = (char *)&out;
outdata[0] = indata[3] ;
outdata[3] = indata[0] ;
outdata[1] = indata[2] ;
outdata[2] = indata[1] ;
return out;
}
inline u_short ByteSwap(u_short in)
{
u_short out;
char *indata = (char *)∈
char *outdata = (char *)&out;
outdata[0] = indata[1] ;
outdata[1] = indata[0] ;
return out;
}
struct RECORD {
UINT32 foo;
UINT32 bar;
CHAR fooword[11];
CHAR barword[11];
UNIT16 baz;
void SwapBytes()
{
foo = ByteSwap(foo);
bar = ByteSwap(bar);
baz = ByteSwap(baz);
}
}
fstream f;
f.open("file.bin", ios::in | ios::binary);
RECORD r;
f.read((char*)&detail, sizeof(RECORD));
r.SwapBytes();
cout << "fooword = " << r.fooword << endl;
uint32 parse_uint32(char* buffer)
{
uint32 x;
// ...
return x;
}
uint32 value = *reinterpret_cast<uint32*>)(ptr); ptr += 4;
return value;
fsf;
f、 打开(“file.bin”,ios::in | ios::binary);
记录r;
f、 读取((字符*)和详细信息,大小(记录));
r、 交换字节();
cout不要直接从文件读入struct!打包可能会有所不同,你必须摆弄pragma pack或类似的编译器特定结构。太不可靠了。很多程序员都能侥幸逃脱,因为他们的代码没有在大量的体系结构和系统中编译,但这并不意味着这是可以做到的
一种很好的替代方法是将头读入缓冲区,然后从3解析,以避免原子操作中的I/O开销,例如读取无符号32位整数
char buffer[32];
char* temp = buffer;
f.read(buffer, 32);
RECORD rec;
rec.foo = parse_uint32(temp); temp += 4;
rec.bar = parse_uint32(temp); temp += 4;
memcpy(&rec.fooword, temp, 11); temp += 11;
memcpy(%red.barword, temp, 11); temp += 11;
rec.baz = parse_uint16(temp); temp += 2;
parse_uint32的声明如下所示:
struct RECORD {
UINT32 foo;
UINT32 bar;
CHAR fooword[11];
CHAR barword[11];
UNIT16 baz;
}
fstream f;
f.open("file.bin", ios::in | ios::binary);
RECORD r;
f.read((char*)&detail, sizeof(RECORD));
cout << "fooword = " << r.fooword << endl;
inline u_int ByteSwap(u_int in)
{
u_int out;
char *indata = (char *)∈
char *outdata = (char *)&out;
outdata[0] = indata[3] ;
outdata[3] = indata[0] ;
outdata[1] = indata[2] ;
outdata[2] = indata[1] ;
return out;
}
inline u_short ByteSwap(u_short in)
{
u_short out;
char *indata = (char *)∈
char *outdata = (char *)&out;
outdata[0] = indata[1] ;
outdata[1] = indata[0] ;
return out;
}
struct RECORD {
UINT32 foo;
UINT32 bar;
CHAR fooword[11];
CHAR barword[11];
UNIT16 baz;
void SwapBytes()
{
foo = ByteSwap(foo);
bar = ByteSwap(bar);
baz = ByteSwap(baz);
}
}
fstream f;
f.open("file.bin", ios::in | ios::binary);
RECORD r;
f.read((char*)&detail, sizeof(RECORD));
r.SwapBytes();
cout << "fooword = " << r.fooword << endl;
uint32 parse_uint32(char* buffer)
{
uint32 x;
// ...
return x;
}
uint32 value = *reinterpret_cast<uint32*>)(ptr); ptr += 4;
return value;
这是一个非常简单的抽象,在实践中更新指针也不需要额外的成本:
uint32 parse_uint32(char*& buffer)
{
uint32 x;
// ...
buffer += 4;
return x;
}
后一种形式允许更干净的代码来解析缓冲区;当您解析输入时,指针会自动更新
同样,memcpy也可以有一个助手,比如:
void parse_copy(void* dest, char*& buffer, size_t size)
{
memcpy(dest, buffer, size);
buffer += size;
}
这种安排的美妙之处在于,您可以使用名称空间“little_endian”和“big_endian”,然后您可以在代码中这样做:
using little_endian;
// do your parsing for little_endian input stream here..
对于相同的代码,很容易切换尾数,但很少需要特性。文件格式通常都有固定的尾数
不要使用虚拟方法将其抽象到类中;这只会增加开销,但如果愿意,可以:
little_endian_reader reader(data, size);
uint32 x = reader.read_uint32();
uint32 y = reader.read_uint32();
reader对象显然只是指针周围的一个薄包装器。size参数将用于错误检查(如果有的话)。对于接口本身来说,这并不是必须的
注意这里的endian选择是如何在编译时完成的(因为我们创建了一个小的_endian _reader对象),所以我们调用虚拟方法开销没有什么特别好的理由,所以我不会使用这种方法。;-)
在这个阶段,没有真正的理由保持“fileformat结构”不变,您可以根据自己的喜好组织数据,而不必将其读入任何特定的结构中;毕竟,这只是数据。当你阅读像图像这样的文件时,你实际上不需要标题。。您应该拥有与所有文件类型相同的图像容器,因此读取特定格式的代码应该只读取文件、解释和重新格式化数据并存储有效负载。=)
我是说,这看起来复杂吗
uint32 xsize = buffer.read<uint32>();
uint32 ysize = buffer.read<uint32>();
float aspect = buffer.read<float>();
uint32 xsize=buffer.read();
uint32 ysize=buffer.read();
float aspect=buffer.read();
代码可以看起来很好,而且开销非常低!如果endi