2009-12-21 238 views
5

我寫了一個C++庫,將我的數據(定製結構集合等)保存到二進制文件中。我目前在我的Windows(XP)機器上使用(即創建消耗)本地文件。爲了簡單起見,我們可以考慮將庫分爲兩部分:一個寫入器(創建文件)和一個讀取器消費者(簡單地從文件讀取數據)。二進制文件和跨平臺兼容性

但是最近,我想也消耗我的Linux機器上(即讀)的數據我的XP機器上的文件我已經創建。我必須指出,在這個階段,這兩臺機器都是PC(因此具有相同的親和力等)。

因爲我是圖書館的創建者,所以我可以構建一個閱讀器(併爲Linux編譯[Ubuntu 9.10])。我的問題,我才走上這條路(建設讀卡器等)是:

假設我已經成功地建立了Linux的讀者,

我可以簡單地複製防空火炮,這是文件的創建 Windows(XP)機器到Linux(Ubuntu 9.10)機器並使用Linux讀取器成功讀取複製的過載文件?

+0

您必須更多地定義「二進制文件」的含義。你使用'fwrite'來寫'struct'等數據嗎?你是否將所有內容分解爲字節然後寫入數據? – 2009-12-21 16:14:19

+1

什麼是所有的SHOUTING?你可以使用星號**來強調**,它的可讀性更強...... – danio 2009-12-22 11:00:28

+0

@danio他來自DOS,所有東西都是CAPITAL – zeitue 2015-08-17 03:32:19

回答

14

對於文件二進制兼容:

  • 字節順序必須匹配(因爲它爲你)
  • 位域打包順序必須是相同的
  • 尺寸和符號性的類型必須相同
  • 編譯器必須對填充和對齊

同樣決定這當然是可能的˚F或者所有這些條件都得到滿足,或者你不會碰到任何他們沒有的情況。不過,至少,我會添加一些理智檢查和/或哨兵成員來檢測問題。

+0

嗨moonshadow,感謝您的反饋。你能詳細說明一些嗎 - 當你有時間的時候,用一個簡單的類包含一個std :: vector 我可以毫不含糊地理解你的意思,理智檢查和/或哨兵成員。我能想到的實現這些檢查的唯一方法是使用limits.h中定義的常量 - 這是你的意思嗎? - 或者你有更優雅的方法? – 2009-12-21 16:29:46

+0

另外,我不知道如何檢查需求2,3和4(您上面列出的)是否成立。我在XP上使用VS2008構建,並在Ubuntu上使用gcc 4.4.1 - 關於如何檢查這些要求是否被違反的任何提示? – 2009-12-21 16:32:04

+2

@貼吧:「哨兵成員」,我的意思是安排寫入文件的頂層結構包含一個已知常數值的成員,並在文件末尾放置一個成員;在加載時,檢查這些成員是否包含您所期望的值 - 這應該會遇到編譯器之間size/padding不同的問題。 – moonshadow 2009-12-21 16:43:05

2

二進制文件應該在具有相同字節的機器上兼容。

您的代碼中可能存在的問題是整數的大小,您不一定假定不同操作系統上的編譯器具有相同的大小int。所以,無論是複製的字節塊和扮演他們,或使用INT16,INT32等

1

如果:

  • 機器具有相同的字節順序(如你說他們有)和
  • 你打開以二進制模式流,因爲文本模式可能會做一些有趣的事情與線兩端和
  • 你乾淨編程,這樣你就不會絆倒實現定義的東西,如比對,數據類型大小和結構包裝,

然後是,你的文件應該是便攜式的。

第三個要點是使文件格式成爲「便攜」文件格式的原因。根據結構中的數據類型,它可能非常容易或有點棘手。位域或從不同類型重新解釋的數據特別棘手。

1

你可以考慮看看Boost Serialization Library。 已經有很多想法,它將爲您處理許多潛在的跨平臺不兼容問題。 當然,對於您的特殊使用情況來說,這可能是過度的,特別是如果您已經實現了讀寫器&。

1

結構是不是的文件格式,你不應該嘗試像這樣使用它們。

當試圖使freadfwrite的結構工作時,有大量的黑客使其工作。您可以使用字節交換整數,以便在小端和大端機器之間共享文件。您將結構更改爲使用固定寬度的整數類型,以便您可以在不同字大小的機器(例如x86和x64機器之間)之間共享。您可以添加編譯器特定的編譯指示來控制結構的填充以在編譯器版本之間共享。

它有效,但它很醜。更何況,容易出錯。

很像The byte order fallacy中的建議,更好的主意是編寫代碼來單獨讀取/寫入字段。通過編寫自己的代碼,可以確保沒有填充,並且可以獨立於整數的本地大小選擇整數大小,並且可以在不進行字節交換的情況下(通過分別讀取/寫入整數字節)來支持兩個字節序。

與hacky方法不同,這很難弄錯。此外,由於您不依賴任何編譯器或特定架構的行爲,您的代碼可以在所有編譯器和體系結構上運行,或者不運行。如果你做得對,你不應該有任何平臺特定的錯誤。

有一個缺點;單獨讀取/寫入字段將比直接使用fread/fwrite慢。您可以設置一個緩衝區(uint8_t buffer[])並將整個數據寫入其中,然後立即寫入所有內容,這可能會有所幫助,但仍然會變慢(因爲您仍然必須將字段移入一次一個緩衝區),但對於大多數目的來說,它仍然足夠快(例外是嵌入式/實時系統或極高性能計算)。