1. 程式人生 > >【記錄】c++實現json格式解析與轉換

【記錄】c++實現json格式解析與轉換

水群的時候看到有人在說bat讀json資料,突然就想寫一個json解析的bat第三方。。。
json語法比較簡單,於是就用有限狀態機來實現了。

下面是轉換效果(左邊是json資料,右邊是轉換為bat命令的結果):轉換效果

順便實現了語法錯誤提示:
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述
這裡寫圖片描述

下面是實現程式碼:

/*
    jsoner
    Version     : 1.0
    Auther      : blackkitty
    Date        : 2017-3-14
    Description : JSON parsing for batch

    batch usage:
        jsoner [-f JSONFile]|[-s JSONString] SaveFile elementName
        call SaveFile
*/
#include <stdio.h> #include <string.h> #include <stdlib.h> #define MATCHED(c,s) (strchr(s,c)!=NULL) /* judge if c is in s */ #define IS_WHITESPACE(c) (MATCHED(c," \t\n")) /* judge if c is a whitespace */ #define IS_KEYCHAR(c) (MATCHED(c,"{}[],:\0")) /* judge if c is a json key character */
enum ContentType {STRING_TYPE, NUMBER_TYPE, BOOL_TYPE, NULL_TYPE, BAD_TYPE}; enum ErrorType{SUCCESS = 0, BAD_CONTENT_TYPE, WRONG_CHARACTER, NO_MATCH, INCOMPLETE}; class ValueName{ public: char key[4096]; ValueName(){ stk = _stk; *stk = 0; memset(key,0,sizeof(key)); } ValueName push(const
char * str){ int t = 0; tail = key+*stk; for(;*str != '\0';t++) *tail++ = *str++; *tail = '\0'; stk[1] = *stk + t; stk++; return *this; } ValueName pop(){ stk--; key[*stk] = '\0'; return *this; } private: int _stk[4096]; int *stk; char *tail; }; struct EC{ ErrorType et; const char * pos; EC(ErrorType _et, const char * _pos):et(_et), pos(_pos){} }; bool isNumber(const char *str); bool matchStr(const char *str1, const char *str2); ContentType typeCheck(const char *str, ErrorType & et); char * loadJsonFile(const char * filepath); char * getContent(const char * str, int & ctSize, ErrorType & et); char * getIndex(int inx); void whitespaceCLR(char *str); int itoa(int x, char *buffer); const char * _ErrorType[] = { "Success.", "Unexpected charater or data type.", "There should not be a '%c'.", "There are no '%c' before to match the '%c'.", "The json string is not complete." }; EC json2cmd(char * jsonStr, FILE * hSaveFile, char * eName); void errorReporter(EC e, const char * json); int main(int argc, char *argv[]) { if(argc >= 5){ char * json = NULL; FILE * fhSave; fhSave = fopen(argv[3], "w+"); if(fhSave == NULL){ return 1; } if(matchStr(argv[1], "-f")){ json = loadJsonFile(argv[2]); if(json == NULL){ fclose(fhSave); return 1; } } else if(matchStr(argv[1], "-s")){ json = argv[2]; } else goto help; errorReporter(json2cmd(json, fhSave, argv[4]), json); fclose(fhSave); return 0; } help:; puts(" jsoner ver 1.0 By blackkitty"); puts(" jsoner [-f JSONFile]|[-s JSONString] SaveFile ObjectName"); puts(""); return 0; } void errorReporter(EC e, const char * json){ if(e.et == SUCCESS){ puts(_ErrorType[e.et]); return; } const char *tmp = "}]"; char * part = (char*)malloc(sizeof(char)*50); char * p = (char*)(json>e.pos-20?json:e.pos-20); int i; for(i = 0;p[i]!='\0'&&i<40;i++){ part[i] = p[i]; } part[i] = '\0'; puts("ERROR:"); printf("%s%s%s\n",p==json?"":"...", part, i<40?"":"..."); printf("%*s\n", (p==json?e.pos-json:23)+1, "^"); switch(e.et){ case BAD_CONTENT_TYPE: /* fallthrough */ case INCOMPLETE: puts(_ErrorType[e.et]); break; case WRONG_CHARACTER: printf(_ErrorType[e.et],*e.pos); puts(""); break; case NO_MATCH: printf(_ErrorType[e.et], "{["[strchr(tmp,*e.pos)-tmp], *e.pos); puts(""); break; default: puts("Unexcepted Error!"); break; } free(part); } /* json2cmd jsont to cmd main loop */ EC json2cmd(char * strJson, FILE * fhSave, char * _objectName) { const char * p; ErrorType et = SUCCESS; char _stk[4096]; /* record "" {} [] : */ char *stk = _stk; ValueName objectName; /* record element name */ objectName.push(_objectName); int _inx[4096] = {0}; /* record list index */ int *inx = _inx; p = strJson; char * tmp; for (; *p;p++) { switch(*p){ case '{': if(*stk == '{') return EC(WRONG_CHARACTER,p); *++stk = *p; break; case '[': if(*stk == '{') return EC(WRONG_CHARACTER,p); *++stk = *p; *++inx = 0; tmp = getIndex(*inx); objectName.push(tmp); free(tmp); break; case '}': if(*stk == '{') stk--; else if(*stk == ':'){ stk--,p--; objectName.pop(); objectName.pop(); } else return EC(NO_MATCH, p); break; case ']': if(*stk == '[') stk--; else return EC(NO_MATCH, p); inx--; objectName.pop(); break; case ':': *++stk = *p; break; case ',': if(*stk == '['){ ++*inx; tmp = getIndex(*inx); objectName.pop(); objectName.push(tmp); free(tmp); } if(*stk == ':'){ stk--; objectName.pop(); objectName.pop(); } break; default: int ctSize = -1; tmp = getContent(p, ctSize, et); if(tmp == NULL) return EC(et, p); if(*stk == '{'){ /* left value */ objectName.push("."); objectName.push(tmp); } else{ /* right value */ fprintf(fhSave, "set \"%s=%s\"\n", objectName.key, tmp); } free(tmp); p+=ctSize; break; } } if(stk != _stk) return EC(INCOMPLETE, p); return EC(SUCCESS, p); } /* whitespaceCLR delete all the whitespace who doesn't effect the value. */ void whitespaceCLR(char * str){ bool flag = false; char *p = str; for (; *str != '\0';str++){ if(*str == '\n') *str=' '; if(*str == '"') flag = !flag; if(flag || (*str!=' ' && *str!='\t' && *str!='\n')) *p++ = *str; } *p = '\0'; } /* itoa int to string. Return length of string. Attention: enough memory of buffer. */ int itoa(int x, char * buffer){ if(x == 0){ *buffer = '0'; return 1; } int len = 0; for(int z=x;z;z/=10) len++; for(int i=len;x;x/=10){ buffer[--i] = '0' + x%10; } return len; } /* getIndex int to index string Attention: free the memory of index string. */ char * getIndex(int inx){ if(inx < 0) return NULL; char * ret = (char *)malloc(sizeof(char)*15); ret[0] = '['; int t = itoa(inx, ret+1); ret[t+1] = ']'; ret[t+2] = '\0'; return ret; } /* isNumber judge if a string is a json number. */ bool isNumber(const char *str){ /* -0.0e+003 */ bool ef, df, ff, fnf, pzf, pnf; ef = false; /* e flag */ df = false; /* dot flag */ ff = true; /* first position flag */ fnf = true; /* first number flag */ pzf = false; /* prefix zero flag */ pnf = false; /* prefix non-zero flag */ for (const char *p = str; !IS_KEYCHAR(*p);p++){ if(MATCHED(*p, "+-")){ if(!ef && *p == '+') return false; if(!ff) return false; ff = false; } else if(MATCHED(*p, "eE")){ if(ff || fnf || ef) return false; ff = fnf = ef = df = true; pnf = false; } else if(*p == '.'){ if(df || ff || fnf || ef) return false; df = fnf = true; } else if(*p >= '0' && *p <= '9'){ if(*p == '0' && !pnf &&!df){ if(pzf) return false; pzf = true; } if(*p != '0') pnf = true; ff = fnf = false; } else{ return false; } } return !fnf; } /* matchStr judge if str2 matched str1. */ bool matchStr(const char * str1, const char * str2){ for (; *str2 != '\0';str1++,str2++) if(*str1 != *str2) return false; return IS_KEYCHAR(*str1); } /* typeCheck get the type of content. */ ContentType typeCheck(const char * str, ErrorType & et){ if(matchStr(str, "true") || matchStr(str, "false")) return BOOL_TYPE; if(matchStr(str, "null")) return NULL_TYPE; if(isNumber(str)) return NUMBER_TYPE; if(*str == '"'){ for (str++; *str != '"';str++); if(IS_KEYCHAR(str[1])) return STRING_TYPE; } et = BAD_CONTENT_TYPE; return BAD_TYPE; } /* getContent Return content and size of content. Return NULL if content do not comply with grammar. Attention: free the memory of content */ char * getContent(const char * str, int & ctSize, ErrorType & et){ char * buffer; /*constent buffer*/ char ctType; /*content type*/ const char * endFlag; switch(ctType = typeCheck(str, et)){ case STRING_TYPE: endFlag = "\""; str++; break; case BOOL_TYPE: /* fallthrough... */ case NULL_TYPE: /* fallthrough... */ case NUMBER_TYPE: endFlag = " \t\n{}[],:\0";break; case BAD_TYPE: /* fallthrough... */ default: return NULL; } /* copy content to buffer */ for(ctSize=0;!MATCHED(str[ctSize], endFlag);ctSize++); buffer = (char *)malloc(sizeof(char)*(ctSize+1)); memcpy(buffer, str, sizeof(char)*ctSize); buffer[ctSize] = '\0'; if(ctType == STRING_TYPE) ctSize++; else ctSize--; return buffer; } /* loadJsonFile return file content. Attention: free the memory of return. */ char *loadJsonFile(const char * filepath){ char * ret; FILE *fh; fh = fopen(filepath, "r"); if(fh == NULL) return NULL; /* get file size */ int fsize = 0; fseek(fh,0,SEEK_SET); fsize = ftell(fh); fseek(fh,0,SEEK_END); fsize = ftell(fh) - fsize; /* load file */ ret = (char *)malloc(sizeof(char)*(fsize+3)); fseek(fh, 0, SEEK_SET); fsize = fread(ret, sizeof(char), fsize-1, fh); ret[fsize] = '\0'; whitespaceCLR(ret); fclose(fh); return ret; }