2010-11-30 89 views
1

我做了comp.lang.C++后,得到了这个字节交换和C++/C

http://groups.google.com/group/comp.lang.c++/browse_thread/thread/afc946826945bdb1/90850f6a0e0edd2d#90850f6a0e0edd2d

但仍然没有答案。

我对二进制读取操作有点困惑。

我想读取流功能的二进制文件。这是一个商业程序(ANSYS)的结果文件,我知道文件的结构,至少从手册中知道。

该文件结构为记录,程序是用fortran编写的。所以该结构是像

记录长度(INT) 虚设整数 数据(可能是int,双) 虚设整数

第一个记录是一个100整数块,其中,这对应于数据在上述表示。

如果我开始阅读文件和读取的第一个值,它是记录长度(整数),我有交换字节来获得100

正确的价值我不明白为什么我有交换字节,因为这个文件是在同一台机器上生成的,并且它们应该使用相同的系统特定例程,所以这应该不是问题,但似乎并非如此。还有其他事情正在发生。我无法理解这一点。软件可以强制交换我很难理解原因的字节吗?

任何意见表示赞赏。

这是一个天真的测试用例

int main() { 
    ifstream myfile; 
    char intBuffer[4]; 
    myfile.open ("truss.rst", ios::binary); 
    myfile.read(intBuffer, sizeof(int)); 
    //cout << *((int*)intBuffer) << endl; 
    // if I do not use this portion- 
    // I do not get what I want 
    char *cptr, tmp; 
    tmp = intBuffer[0]; 
    intBuffer[0] = intBuffer[3]; 
    intBuffer[3] = tmp; 
    tmp = intBuffer[1]; 
    intBuffer[1] = intBuffer[2]; 
    intBuffer[2] = tmp; 
    // ----------------------------- 
    cout << *((int*)intBuffer) << endl; 

    myfile.close(); 
    return 0; 
} 

最佳, U.

+1

你在c.l.C++上收到的答案有什么问题?对我来说似乎很好。 – 2010-11-30 08:56:25

+0

你是否按照说明和搜索“endian”(并阅读它)?维基百科有详细解释。 – 2010-11-30 08:59:23

+0

有趣的是,你可以在函数体的第4行使用sizeof(int),但是在其他地方将其硬编码为4 ... 你也声明了cptr,然后从不使用它。 如果你想知道为什么文件不是以“本机”格式写的,你看过写入文件的例程吗? – CashCow 2012-11-12 09:45:36

回答

2

也许该软件以支持小/大端架构做这种“奇怪”的操作(字节顺序不同)。

结论:

  • 在两个不同的机器(小/大端)如果插入文件的二进制信息,具有相同的输入,文件可以是不同的。
+0

这里有一些参考,如果你想确定你使用的架构:http://stackoverflow.com/questions/2100331/c-macro-definition-to-determine-big-endian-or-little-endian-machine – Phong 2010-11-30 09:00:46

1

一些文件格式要求的字节顺序是在一个单一的方式通常大端因为这是网络秩序等小尾数x86s这些文件都写在他们的整数字节交换和读取

4

时换回不管它的格式显然是跨机器一致的(如果你不能在另一台机器上打开文件,这将是有趣的)。

因此,字节排序和数据类型的大小都必须在格式中定义,并且当您想要读取这种格式时,您需要使用这些字节顺序和数据类型大小。

6

这并不仅仅取决于您正在使用的机器。如果Fortran基础结构以大端而不是小端来编写整数,则无论操作系统是什么,都必须处理。

我建议你使用ntohl()ntohs()函数,它比你的交换例程更清晰。

1

这是endian problem。英特尔CPU使用小端。 “网络字节顺序”/ SPARC/Motorola使用big endian。许多传统的便携式应用程序以big endian存储文件以实现互操作性。

1

当您自愿强制一个字节顺序时有一些众所周知的时间:当数据打算在开始时不知道字节顺序的机器之间交换时,如通过网络。这就是为什么有C原语像ntohlhtonl:如果网络endianess是相同的机器endianness这些什么也不做,否则他们交换字节。如果文件应该在机器之间进行交换,那么可能会有类似的情况。

但真正的问题是:数据块中是否还有相同的字节交换。如果不是,那确实有些奇怪,0可能只是填充,而不是格式的所有部分。如果字节交换也出现在数据块中,则可能是故意完成的。

最便携的解决方案当然是逐字节读取文件并手动组装数据,因此您可能能够处理大于uint32_t的整数。

在阅读双打时,也可能会遇到一些麻烦,因为字节排序也可能是交换的,而且它们不容易手工组装。

下面的代码应该作为你想改变字节顺序的任何类型的模板,包括double。

#include <stdio.h> 
#include <arpa/inet.h> 
#include <stdint.h> 

template <class builtin> 
builtin ntoh(const builtin input) { 
    if ((int)ntohs(1) != 1){ 
     union { 
      char buffer[sizeof(builtin)]; 
      builtin data; 
     } in, out; 
     in.data = input; 
     for (int i = 0 ; i < sizeof(builtin); i++){ 
      out.buffer[i] = in.buffer[sizeof(builtin) - i - 1]; 
     } 
     return out.data; 
    } 
    return input; 
} 

main(){ 
    printf ("78563412 expected, got: output= %x\n", ntoh<uint32_t>(0x12345678)); 
} 

它不会提供最佳的性能,look here以获取本机类型更好的性能。

3

软件采用特定的字节顺序使二进制文件更加便携,即使该软件尚不支持其他平台也可能永远不会。同样,软件可能会使用为便携性设计的序列化库。像ntohl()等例行程序可能会帮助您恢复您想要的订单。

-1

htonl(主机到网络长)和htons(主机到网络短路)将从您所在的任何平台转到big-endian。那是因为在那些日子里,大多数网络主机都运行一种使用本地big-endian的UNIX形式。

ntohl和ntohs会将大端转换为本地,无论您的平台如何。如果你在一个大的endian平台上,这些将是一个无操作。

除了字节顺序,另一个潜在的可移植性问题是短和长的大小。 ntohl将读取4个字节并转换为32位整数。因此目标int至少需要32位来保存它,它不需要完全是这个长度。 ntohs读取2个字节并转换为16位短整型。请注意,如果您的本地平台确实使用32位以上或16位,那么如果它们是有符号整数(因为ntohl的实际类型未经签名),您必须管理“符号”问题。

随着现在包括Linux在内的更多机器使用带有小端符号的英特尔处理器,现在更频繁地使用它作为“默认”格式并获得大端格式的更改。在这种情况下,您可能希望编写自己的宏来转换为小端(在已经是小端平台的平台上,它们将不能运行)。

对于实际反转字节,顺便说一句,你可以使用std :: reverse,并且你需要两个指针,一个指向第一个字节,另一个指向最后一个字节。

你也可以实现“字节交换”,然后你的右指针应该在最后一个字节上,而不是一个接一个。您BYTESWAP这样的:

void byteswap(unsigned char & byte1, unsigned char & byte2) 
{ 
    byte1 ^= byte2; 
    byte2 ^= byte1; 
    byte1 ^= byte2; 
} 

在C(而不是C++)实现你会使用一个指针,而不是一个引用作为参数。

在你给实际的例子,该文件似乎是由它的规格存储在32位大端(即网络)字节顺序,所以你可以在这里简单地再用ntohl使用,然而再用ntohl需要一个unsigned int作为一个参数。因此,纠正你的代码:

uint32_t count = 0; 
myfile.open ("truss.rst", ios::binary); 
myfile.read(reinterpret_cast<char*>(&count), sizeof(uint32_t)); 
    // ideally validate that the read succeeded 
count = ntohl(count); 

一个iostream中,你必须做投weakenesses在我看来。谁写它从来没有真正喜欢二进制I/O的概念。当然,如果你用C而不是C++编写,你可以使用FILE*fread