- 精通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下却会导致文件保存失败。对于二进制文件的读写,完全是指针的操作,所以一定要把指针操作搞清楚才行。