2010-11-30 66 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