C语言,用json文件存储tree
C语言,用json文件存储tree:
1.读取和保存都用递归算法遍历,可以保存任意树结构,相比用INI文件,一致性更好,当然用数据库功能更多更好
2.json数据全部用节点最方便,缺点存储的json文件末端有一个空的{},不太美观
3.json文件读入时最好判断编码,尤其是对手工编辑的文件
4.json的root节点没必要创建在树上,除非要展现多个json文件在同一树上
5.测试程序用VS2010的MFC模板,加入CJSON文件,主要程序段如下:
//保存树到json中
cJSON * CFileView::AddJsonNode(HTREEITEM hItem, cJSON *NodePrev) { char text[256]; //no need CString str = m_wndFileView.GetItemText(hItem); HWND hWndTree = m_wndFileView.m_hWnd; //no need HTREEITEM hChild = TreeView_GetChild(hWndTree, hItem); //no need char *p = WcharToChar(str.GetBuffer(0), CP_UTF8); cJSON *node; //if(hChild != NULL) { node = cJSON_CreateObject(); cJSON_AddItemToObject(NodePrev, p, node); delete p; return node; } /*else //没必要,反正要有个多余的,用{}也可以,所以都用Obj { cJSON_AddStringToObject(NodePrev, p, " "); delete p; return NodePrev; }*/ PRINT(_T("\r\n %s"), str.GetBuffer(0)); //no need } void CFileView::RecursionTreeSave(HTREEITEM hItem, cJSON *NodePrev) { if (!hItem) return; HWND hWndTree = m_wndFileView.m_hWnd; cJSON *node = AddJsonNode(hItem, NodePrev); //递归函数中增加节点必须在子节点前 HTREEITEM hChild = TreeView_GetChild(hWndTree, hItem); RecursionTreeSave(hChild, node); //进入子节点 HTREEITEM hSibling = TreeView_GetNextSibling(hWndTree, hItem); RecursionTreeSave(hSibling, NodePrev); //DeleteNode(hItem); //递归函数中删除只能处理当前节点,放在子节点处理后再处理当前节点 return; }
//======
。。。。。。。
//下面是把树保存为json文件
HTREEITEM hRoot = m_wndFileView.GetRootItem(); CFileJson fjson(NULL); if(fjson.root != 0) cJSON_Delete(fjson.root); fjson.root = 0; // 创建 JSON root fjson.root = cJSON_CreateObject(); RecursionTreeSave(hRoot, fjson.root); fjson.Save("r:\\tree.json");
//======================
//从json读取数据生成树
HTREEITEM CFileView::AddTreeNode(cJSON *jNode, HTREEITEM hParen, HTREEITEM hAfter) { cJSON * node = jNode; if(node == NULL) return NULL; wchar_t *p = CharToWchar(node->string, CP_UTF8); HTREEITEM hItem; if(node->child) { hItem = m_wndFileView.InsertItem(p, 0, 0, hParen, hAfter); delete p; } else { hItem = m_wndFileView.InsertItem(p, 1, 1, hParen, hAfter); delete p; } return hItem; } void CFileView::RecursionTreeLoad(cJSON *jNode, HTREEITEM hParen, HTREEITEM hAfter) { cJSON * node = jNode; if(node == NULL) return; HWND hWndTree = m_wndFileView.m_hWnd; HTREEITEM hItem = AddTreeNode(jNode, hParen, hAfter); //递归函数中增加节点必须在子节点前 cJSON* jNodeChild = node->child; RecursionTreeLoad(jNodeChild, hItem, NULL); //进入child节点 cJSON* jNodeNext = node->next; RecursionTreeLoad(jNodeNext, hParen, hItem); //进入Next节点 return; }
//===================
// 下面是从json文件读取数据,用来生成树,root节点不显示,从root的子节点开始
。。。。。
CFileJson fjson("r:\\tree1.json"); if(fjson.root == 0) { PRINT(_T("\r\n Open fail!")); return; } m_wndFileView.DeleteAllItems(); cJSON *node = fjson.root->child; //XGZ:ROOT NOT FOR TREE NODE RecursionTreeLoad(node, NULL, NULL); HTREEITEM hRoot = m_wndFileView.GetRootItem(); m_wndFileView.Expand(hRoot, TVE_EXPAND);
//===Json的c++封装,只是为了可靠释放
class CFileJson { public: CFileJson(const char* filepathname); ~CFileJson(); cJSON * root; int Save(const char* filepathname); };
//另外要注意文件打开时判断编码,保存时默认字符串生成UTF-8,
//若只是自己的程序存取自己的文件,这些判断转换都不用,只要打开文件直接读取就行了
CFileJson::CFileJson(const char* filepathname) { root = 0; if(filepathname == NULL) return; wchar_t* pwText; int length; FILE* fp = fopen(filepathname, "rb"); if (fp) { fseek(fp, 0, SEEK_END); //到文件末尾,0 succes int FileLen = ftell(fp); //到末尾后的位置就是文件长度 char* p1; wchar_t* p2; p1 = new char[FileLen + 16]; fseek(fp, 0, SEEK_SET); //回到0位置, 0 succes length = fread(p1, 1, FileLen + 16, fp); fclose(fp); *(p1 + length) = 0; *(p1 + length + 1) = 0; *(p1 + length + 2) = 0; *(p1 + length + 3) = 0; if (((unsigned char)*p1 == 0xff) && ((unsigned char)*(p1 + 1) == 0xfe)) //UTF-16LE { for (int i = 0; i < length; i += 2) { *(p1 + i) = *(p1 + i + 2); *(p1 + i + 1) = *(p1 + i + 3); } pwText = (wchar_t*)(p1); } else if (((unsigned char)*p1 == 0xfe) && ((unsigned char)*(p1 + 1) == 0xff)) //UTF-16BE { for (int i = 0; i < length; i += 2) { *(p1 + i) = *(p1 + i + 3); *(p1 + i + 1) = *(p1 + i + 2); } pwText = (wchar_t*)(p1); } else if (((unsigned char)*p1 == 0xef) && ((unsigned char)*(p1 + 1) == 0xbb) && ((unsigned char)*(p1 + 2) == 0xbf))//UTF-8 BOM { p2 = CharToWchar(p1 + 3, CP_UTF8); //程序统一转换为UTF16LE,这里其实是不需要的, delete p1; pwText = (wchar_t*)p2; //UTF-8 BOM 可以和下面UTF-8 一样,偏移3字节,直接解析 } else //ANSI, UTF-8 { int checknum = FileLen; //1000000; //XGZ:NEED CHECK if (checknum > length) checknum = length; if (IsUTF8Text(p1, checknum)) //UTF-8 { root = cJSON_Parse(p1); delete p1; return; //XGZ: THIS APP, CP_UTF8 NO NEED CHANGE TO UTF16LE //p2 = CharToWchar(p1, CP_UTF8); //delete p1; //pwText = (wchar_t*)p2; } else //ASCII CP_ACP { p2 = CharToWchar(p1, CP_ACP); delete p1; pwText = (wchar_t*)p2; } } char * pdata = WcharToChar(pwText, CP_UTF8); root = cJSON_Parse(pdata); //XGZ:DEFAULT CODE CP_UTF8 delete pdata; } return; } CFileJson::~CFileJson() { if(root != 0) { cJSON_Delete(root); root = 0; } return; } int CFileJson::Save(const char* filepathname) { FILE *fp = fopen(filepathname,"wb"); if(root != 0) { char *pdata = cJSON_Print(root); //XGZ:DEFAULT CODE CP_UTF8 if(pdata != NULL) { fwrite(pdata,sizeof(char), strlen(pdata), fp); } free(pdata); //cJSON use malloc //static void *(*cJSON_malloc)(size_t sz) = malloc; } fclose(fp); return 0; }
程序读取json文件后显示在tree上

json文件:


浙公网安备 33010602011771号