二進制配置文件為什么比json等配置文件高效
項目中用spine做動畫,spine可以導出json和二進制的動畫配置文件,蛋疼的是spine官方竟然沒有提供c的二進制配置解析代碼,更沒有提供它二進制文件的格式說明。為了動畫加載的更快,只能自己把json用python轉成二進制文件(格式自定),再自行實現解析二進制的c代碼了。經過小心謹慎的代碼實現后,測試結果是二進制配置文件比json文件大小小了1半以上,並且動畫創建速度快1倍以上。下文簡單總結了一下二進制配置文件的優點。
1.什么是二進制配置文件
當然,一切文件在計算機中都是以二進制(01)格式存儲的,那么二進制配置文件是指什么呢?比如要存儲
{ "time" = 133, "color" = [233, 0, 0], "pos" = [34, 22] }
json一般以utf-8格式保存成文本,那么什么是utf-8呢?它是unicode編碼的一種實現形式,具體解釋請參考UTF-8編碼規則(轉),中日韓uincode編碼參考。也就是說,像程序中的數字類型133,233,22等,一個uint8就能存儲下了,可是133在json中卻占了3個字節,要是存個12.432312等數據要占用更多的空間。而本文所說的二進制配置,直接存133等的uint8二進制編碼0x85,這樣便減少了一部分文件大小。編碼與解碼可以商量好,比如time,編解碼都以t代替,又可以節省一部分空間,甚至可以不存儲time,color,pos等key,直接順序在配置中寫value,解碼時直接讀value(為了說的清楚,后面的例子保留了key)這樣又可以減少配置文件的大小。
2.二進制配置文件優點與缺點
優點已經說了,2個優點:文件小、解析快。為啥文件小上面已經說了。至於為啥解析快呢,首先,因為文件小,io時間需要的就少。其次,少了把配置文件解析成json對象或xml對象的中間步驟,直接讀這個二進制文件就可以,我覺得順着讀二進制文件可以達到最快的速度。
缺點也有2個:難以直接查看、通用性差。json或xml等配置文件可以直接看出來配置的是神馬,可是這個二進制就啥也看不出來了。通用性差是因為要達到高效的解析,最好順着解析,捋一遍配置文件就完了,所以解析二進制的代碼難以公用。
3.讀寫二進制例子
比如上面的那個json文件,怎么搞成二進制配置呢,可以先規定好數據的類型,比如s為string,i為number,f為float。string還要告訴解析時字符串有多長,比如time就是s4time。上面的例子要寫二進制就是s4timei133s5colori244i0i0s3posi34i22,這肯定不是最優的,就是為了說明怎么讀寫二進制而已。
(1)python寫二進制文件
python寫二進制文件用到了 struct.pack,讀二進制用 struct.unpack(),具體怎用用struct請參考 使用Python進行二進制文件讀寫
| Format | C Type | Python | 字節數 |
|---|---|---|---|
| x | pad byte | no value | 1 |
| c | char | string of length 1 | 1 |
| b | signed char | integer | 1 |
| B | unsigned char | integer | 1 |
| ? | _Bool | bool | 1 |
| h | short | integer | 2 |
| H | unsigned short | integer | 2 |
| i | int | integer | 4 |
| I | unsigned int | integer or long | 4 |
| l | long | integer | 4 |
| L | unsigned long | long | 4 |
| q | long long | long | 8 |
| Q | unsigned long long | long | 8 |
| f | float | float | 4 |
| d | double | float | 8 |
| s | char[] | string | 1 |
| p | char[] | string | 1 |
| P | void * | long |
import struct
def writeBinary():
binaryFile = open(r"./binary.obj","wb");
binaryFile.write(struct.pack("i", 4));
binaryFile.write(struct.pack("4s", "time"));
binaryFile.write(struct.pack("i", 133));
binaryFile.write(struct.pack("i", 5));
binaryFile.write(struct.pack("5s", "color"));
if __name__ == "__main__":
writeBinary();
執行完會產生下面的二進制文件

(2)c++讀二進制文件
#include <iostream>
using namespace std;
char * data = nullptr;
char * getFileContent(const char * filePath) {
FILE *pFile=fopen(filePath, "r");
char * pBuf;
fseek(pFile, 0, SEEK_END);
int len = ftell(pFile);
pBuf = new char[len+1];
rewind(pFile);
fread(pBuf, 1, len, pFile);
pBuf[len] = 0;
fclose(pFile);
return pBuf;
}
int8_t readByte() {
return (int8_t)*data++;
}
int32_t readInt() {
int32_t val = 0;
char * pd = (char*)&val;
*pd = readByte();
*(pd + 1) = readByte();
*(pd + 2) = readByte();
*(pd + 3) = readByte();
return (int32_t)val;
}
std::string readString() {
int32_t strLen = readInt();
std::string str = std::string(data, strLen);
data = data + strLen;
return str;
}
int main() {
data = getFileContent("/Users/abc/PycharmProjects/BinaryTest/binary.obj");
std::string timeStr = readString();
int timeValue = readInt();
std::string colorStr = readString();
std::cout << "timeStr is " << timeStr << std::endl;
std::cout << "colorStr is " << colorStr << std::endl;
std::cout << "timeValue is " << timeValue << std::endl;
free(data);
return 0;
}
結果為

