用C 優雅的實現物件到檔案的序列化 反序列化

2021-09-24 16:15:14 字數 2775 閱讀 5344

需求

.在寫**的過程中,經常會需要把**層面的物件資料儲存到檔案,而這些資料會以各種格式儲存.例如:json,xml,二進位制等等.最近恰好就需要把物件以二進位制儲存到硬碟.這是乙個很簡單的需求,相比json,xml格式,二進位制是直接把位元組copy到硬碟,沒有中間商賺差價,所以這實現起來相對容易.

實現

struct vec3
.上面是乙個簡單的三維向量結構體,如何把它序列化到檔案呢?

vec3 v;

v.x = 1.0f;

v.y = 2.0f;

v.z = 3.0f;

os.write((const char *)&v, sizeof(vec3));

.上述是序列化vec3物件資料到檔案的**,非常直接.它的記憶體布局是3個浮點型變數緊湊排列,要把它儲存到硬碟,只要從頭到尾按位元組拷貝即可.但是,在實際開發中,要序列化的物件不可能全部都是記憶體緊湊排列的,例如stl容器.

std::vectorvec;
.如果將容器變數從頭到尾拷貝到檔案,必然會出現錯誤.因為容器內部通過乙個指標來訪問儲存的物件,而直接拷貝這個容器,只會把指標拷貝,指標指向的資料卻丟失了.但是,容器提供了乙個可以直接訪問指標指向資料的介面,我們可以通過這個介面得到資料然後直接拷貝.

os.write((const char *)&vec, vec.size() * sizeof(vec3));        //  錯誤, 僅拷貝指標

os.write((const char *)vec.data(), vec.size() * sizeof(vec3)); // 正確, 資料被完全拷貝

.通過這個方法就可以得到正確的拷貝結果了.通常,好的做法是將序列化和反序列化封裝成介面,以便於使用,如何封裝介面,就是這篇文章的主題.

.從上述兩個例子可以發現,對於單體物件和陣列物件,編寫的**是不一樣的,單體物件直接拷貝,陣列物件需要通過.data()取得資料位址再進行拷貝.而考慮到還有巢狀陣列物件std::vector>.對於巢狀陣列序列化的**可能如下:

std::vector> vec2;

for (auto & vec: vec2)

.可以發現,對巢狀陣列物件的序列化**跟上述2種物件又不一樣,考慮到還有n層巢狀的陣列物件.此外,在c++中有乙個 可平凡複製的概念 ,通俗的說,就是可以直接按位元組拷貝的結構稱之為 可平凡複製 ,上述的vec3則是乙個可平凡複製結構,而stl容器則不是可平凡複製結構,除此之外還有更多不可平凡複製且非容器的結構,故此,如果要封裝介面,除了區分單體物件和陣列物件,還要區分可平凡複製和不可平凡複製.

void serialize(std::ostream & os,   const type & val);  //  序列化

void deserialize(std::istream & is, type & val); // 反序列化

.上面是比較理想的介面原型,序列化/反序列化各乙個介面,腦補一下,這兩個介面的實現應該是怎樣的?最直接的實現是對每一種型別過載乙個定義,例如:

//  string

void serialize(std::ostream & os, const std::string & val)

// vectorvoid serialize(std::ostream & os, const std::vector& val)

// vectorvoid serialize(std::ostream & os, const std::vector& val)

}// 介面呼叫

std::string str;

std::vectorvecint;

std::vectorvecstr;

serialize(os, str);

serialize(os, vecint);

serialize(os, vecstr);

如果有想學習程式設計的初學者,可來我們的c/c++技術學習扣qun的哦:前面是58,93後面是4,83-89裡面免費送整套系統的c/c++教程!

.從上面可以看出,介面統一,使用方便.但是對每一種型別都過載,要寫的**實在太多了,萬一要序列化乙個多層巢狀陣列,會寫的懷疑人生.借助c++強大的語言特性,這一切都可以一步到位.

//  pod

template , int> n = 0>

void serialize(std::ostream & os, const t & val)

// 容器

template ().begin())> &&

std::is_same_v().end())>, int> n = 0>

void serialize(std::ostream & os, const t & val)

}// pod

template , int> n = 0>

void deserialize(std::istream & is, t & val)

// 容器

template ().begin())> &&

std::is_same_v().end())>, int> n = 0>

void deserialize(std::istream & is, t & val)

}

.以上實現可序列化任意 可平凡拷貝 結構,並且也可序列化任意巢狀層數的stl風格陣列.而對於 不可平凡複製 結構,只需要針對該結構過載即可.借助c++強大的型別推導機制和sfinea機制,可保證型別安全又具備可擴充套件性.

C 序列化物件到檔案和從檔案發序列化物件

序列化物件和發序列化物件 程式應用過程中,經常會把物件序列化到到檔案中,以儲存資料。然後再從檔案中反序列化出來。具體應用如下 1,構造乙個類 注意一定要加上 serializable 表示可序列。serializable class parameter public string name publ...

用C 實現Web檔案的上傳

在web程式設計中,我們常需要把一些本地檔案上傳到web伺服器上,上傳後,使用者可以通過瀏覽器方便地瀏覽這些檔案,應用十分廣泛。那麼使用c 如何實現檔案上傳的功能呢?下面筆者簡要介紹一下。首先,在你的visual c web project中增加乙個上傳用的web form,為了要上傳檔案,需要在t...

用C 實現Web檔案的上傳

在 web程式設計中,我們常需要把一些本地 檔案上傳到 web伺服器上,上傳後,使用者可以通過瀏覽器方便地瀏覽這些 檔案,應用十分廣泛。那麼使用c 如何 實現檔案 上傳的功能呢?下面筆者簡要介紹一下。首先,在你的visual c web project 中增加乙個 上傳用的 web form,為了要...