- 精通Cocos2d-x游戲開發(進階卷)
- 王永寶
- 994字
- 2020-11-28 22:36:57
1.5 讀寫二進制文件
嚴格來說所有的文件都是二進制格式,二進制文件一般會對應一些數據結構。可以直接將自定義的數據結構存儲到文件中,讀寫都很方便,而且在讀寫的過程中,可以很輕松地添加加密解密的操作,安全性高,效率也快,不需要像前面的其他格式,逐個解析數據,而是直接取出內存,然后結構體強制轉換,直接使用。由于是自定義的格式,所以可以自定義各種后綴,如以dat結尾,或者以sys結尾都可以。
首先來了解一下簡單數據結構讀寫,現在定義一個數據結構,來存儲玩家的名字、等級、金幣、經驗等信息。注意,數組必須是固定長度的,如果是不固定長度的數組,可以參考下面讀取動態數據結構的方法。
//定義玩家信息結構體 struct PlayerInfo { char Name[32]; int Level; int Money; int Exp; }; //填充這個結構體 PlayerInfo info; memset(&info, 0, sizeof(PlayerInfo)); strncpy(info.Name, "BaoYe", sizeof(info.Name)); info.Level = 10; info.Money = 888; info.Exp = 0; //注意這里是getWritablePath,獲取一個可寫的路徑 string path = FileUtils::getInstance()->getWritablePath(); path.append("user.dat"); //文件打開的方式是wb二進制方式寫入 FILE* fd = fopen(path.c_str(), "wb"); if (NULL == fd) { return false; } //寫入文件并關閉 int count = fwrite((char*)&info, 1, sizeof(PlayerInfo), fd); fclose(fd); CCLOG("Write File %s\n Size %d", path.c_str(), count);
上面的代碼將信息寫入到文件保存起來了,接下來將其讀出來。
string path = FileUtils::getInstance()->getWritablePath(); path.append("user.dat"); PlayerInfo info; //文件打開的方式是rb二進制讀取 FILE* fd = fopen(path.c_str(), "rb"); if (NULL ! = fd) { //取出來就可以用了 fread(reinterpret_cast<char*>(&info), 1, MAX_BUFFER_SIZE, fd); } CCLOG("Read File %s\n name %s level %d money %d exp %d", path.c_str(), info.Name, info.Level, info.Money, info.Money);
接下來看一下動態數據結構讀寫,我們定義一個背包的數據結構,動態數據的讀寫會稍微麻煩一些,也比較容易出錯,但還是可以輕松搞定的。因為是動態的,所以數據結構盡量簡化一些。
//物品信息結構體 struct Item { int id; int count; }; char buf[MAX_BUFFER_SIZE]; //先把背包中物品的總數寫入 *(int*)(buf) = 10; //后面的內容是背包中所有物品的信息 Item* bag = (Item*)(buf + sizeof(int)); for (int i = 0; i < 10; ++i) { bag[i].id = i; bag[i].count = 3; } string path = FileUtils::getInstance()->getWritablePath(); path.append("bag.dat"); FILE* fd = fopen(path.c_str(), "wb"); if (NULL == fd) { return false; } //寫入文件并關閉,寫入的長度是動態計算出的內存大小 //一共寫入了1個int和10個Item int count = fwrite(buf, 1, sizeof(int)+sizeof(Item)* 10, fd); fclose(fd); CCLOG("Write File %s\n Size %d", path.c_str(), count);
接下來就是把它讀出來!其實在讀的時候,應該做一個這樣的判斷,假設讀取失敗,說明存檔異常,或者是沒有存檔,這時候應該創建一個默認的存檔。
char buf[MAX_BUFFER_SIZE]; string path = FileUtils::getInstance()->getWritablePath(); path.append("bag.dat"); CCLOG("Read File %s", path.c_str()); //文件打開的方式是rb二進制讀取 FILE* fd = fopen(path.c_str(), "rb"); if (NULL ! = fd) { fread(buf, 1, MAX_BUFFER_SIZE, fd); //取出第一個字段,判斷有多少個物品 int count = *(int*)buf; CCLOG("Item Count %d", count); Item* items = (Item*)(buf + sizeof(int)); for (int i = 0; i < count; ++i) { //遍歷取出所有的物品 Item item = items[i]; CCLOG("Item %d is %d, count %d", i + 1, item.id, item.count); } }
需要特別注意的一點是,使用fopen()方法打開,必須使用fclose()方法關閉,特別是在需要保存文件時,如果忘記調用fclose()方法,在Windows下不會有問題,但是在iOS下卻會導致文件保存失敗。對于二進制文件的讀寫,完全是指針的操作,所以一定要把指針操作搞清楚才行。