官术网_书友最值得收藏!

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下卻會導致文件保存失敗。對于二進制文件的讀寫,完全是指針的操作,所以一定要把指針操作搞清楚才行。

主站蜘蛛池模板: 航空| 鄂伦春自治旗| 永川市| 云霄县| 宜章县| 潢川县| 洪雅县| 哈巴河县| 横山县| 天全县| 于田县| 叙永县| 定襄县| 叙永县| 锡林郭勒盟| 牡丹江市| 玉树县| 蒙山县| 元氏县| 惠安县| 绥宁县| 马尔康县| 香港| 丹阳市| 巨野县| 锡林郭勒盟| 措勤县| 铅山县| 新和县| 东台市| 广安市| 镇雄县| 南靖县| 内江市| 昌江| 新余市| 武功县| 河池市| 滦南县| 集贤县| 上栗县|