引言  - 以前那些系列

  长活短说, 写的最终 scjson 纯c跨平台引擎, 希望在合适场景中替代老的csjon引擎, 速度更快, 更轻巧.

下面也是算一个系列吧. 从cjson 中得到灵感, 外加上很久以前自己写的json引擎.  具体的可以看看下面链接.

  C json实战引擎 一 , 实现解析部分

  C json实战引擎 二 , 实现构造部分

  C json实战引擎 三 , 最后实现部分辅助函数

代码也许有点乱, 但是相信你看 cjson那个源码, 那就更乱了. 有些代码不讲道理, 好吃的东西都得让你难受一下. 才有转折.

本文是为了scjson 手写引擎添加注释解析功能. 再在跨平台上再做一些修改, 最终给出完整的测试demo.

  c json实战引擎四 , 最后❤跳跃 (这就是程序语言设计中自举例子)

本文最终的资源  test_scjson.zip

前言  - 那就从json文件看起

  先看我们需要处理的 goods.json 文件

  1. [
  2. /*
  3. * 物品定义处:
  4. * 物品名,品质,作用值,加血,加魔,加攻击,加防御,加速度,加幸运,价格,\
  5. * 占用包裹,加暴击,拥有量,最大拥有,可买(1可买,0不可买),可卖(1可卖, 0不可买)
  6. */
  7. ["小灵芝", "低级★", 1, 50, 0, 0, 0, 0, 0, 20, 1, 0, 3, 99, 1, 1],
  8. ["中灵芝", "中级★★", 1, 100, 0, 0, 0, 0, 0, 40, 2, 0, 1, 99, 1, 1],
  9. ["大灵芝", "高级★★★", 1, 200, 0, 0, 0, 0, 0, 80, 3, 0, 1, 99, 1, 1],
  10. ["卤肉 ", "初级★", 1, 80, 0, 0, 0, 0, 0, 30, 1, 0, 0, 99, 1, 0],
  11. ["小鸭脖", "初级★", 1, 100, 0, 0, 0, 0, 0, 35, 1, 0, 5, 99, 1, 1],
  12. ["小蓝瓶", "初级★", 1, 0, 50, 0, 0, 0, 0, 20, 1, 0, 0, 99, 1, 1],
  13. ["中蓝瓶", "中级★★", 1, 0, 100, 0, 0, 0, 0, 40, 2, 0, 0, 99, 1, 1],
  14. ["大蓝瓶", "高级★★★", 1, 0, 200, 0, 0, 0, 0, 80, 3, 0, 1, 99, 1, 1],
  15. ["金鳌 ", "高级★★★", 3, 0, 0, 5, 0, 0, 0, 200, 2, 0, 2, 99, 1, 1],
  16. ["野山椒", "中级★★", 3, 0, 0, 2, 0, 0, 0, 80, 2, 0, 1, 99, 1, 1],
  17. ["巨蜥肉", "高级★★★", 3, 0, 0, 0, 10, 0, 0, 100, 3, 0, 1, 99, 1, 1],
  18. ["龙血 ", "神级★★★★★", 3, 300, 100, 2, 2, 2, 0, 600, 5, 0, 1, 99, 0, 0],
  19. ["龙肉 ", "神级★★★★★", 3, 0, 0, 10, 20, 10, 0, 800, 5, 0, 1, 99, 0, 0],
  20. ["木剑 ", "初级★", 2, 0, 0, 10, 0, 0, 0, 50, 1, 0, 1, 1, 0, 0],
  21. ["木衣 ", "初级★", 2, 0, 0, 0, 10, 0, 0, 50, 1, 0, 1, 1, 0, 0],
  22. ["木鞋 ", "初级★", 2, 0, 0, 0, 0, 10, 0, 50, 1, 0, 1, 1, 0, 0],
  23. ["屠龙剑", "神级★★★★★", 2, 0, 0, 100, 0, 0, 0, 10000, 0, 0, 0, 1, 1, 1],
  24. ["龙皮铠甲", "神级★★★★★", 2, 0, 0, 0, 200, 0, 0, 10000, 0, 0, 0, 1, 1, 1],
  25. ["涅槃丹", "神级★★★★", 3, 100, 100, 100, 100, 20, 10, 5000, 1, 1, 0, 1, 1, 1]
  26. ]

扯一点我是用notepad++ 编辑的, 请安装 JsTool 插件处理json很好用

切入正题我们处理思路是, 在文件读取的时候, 去掉无效字符和注释字符. 主要code思路如下

  1. // 从json文件中解析出最简json数据
  2. static tstr_t _cjson_newfile(const char * path) {
  3. char c, n;
  4. tstr_t tstr;
  5. FILE * txt = fopen(path, "r");
  6. if (NULL == txt) {
  7. CERR("fopen r %s is error!", path);
  8. return NULL;
  9. }
  10.  
  11. //这里创建文本串对象
  12. tstr = tstr_new(NULL);
  13.  
  14. while ((c = fgetc(txt)) != EOF) {
  15. // step 1 : 处理字符串
  16. if (c == '"') {
  17. tstr_append(tstr, c);
  18. for (n = c; ((c = fgetc(txt)) != EOF) && (c != '"' || n == '\\'); n = c)
  19. tstr_append(tstr, c);
  20. if (EOF != c)
  21. tstr_append(tstr, c);
  22. continue;
  23. }
  24.  
  25. // step 2 : 处理不可见特殊字符
  26. if (c < '!')
  27. continue;
  28.  
  29. if (c == '/') {
  30. // step 3 : 处理 // 解析到行末尾
  31. n = fgetc(txt);
  32. if (n == '/') {
  33. while ((c = fgetc(txt)) != EOF && c != '\n')
  34. ;
  35. continue;
  36. }
  37.  
  38. // step 4 : 处理 /*
  39. if (n == '*') {
  40. while ((c = fgetc(txt)) != EOF) {
  41. if (c == '*') {
  42. n = fgetc(txt);
  43. if (n == '/')
  44. break;
  45. ungetc(n, txt);
  46. }
  47. }
  48. continue;
  49. }
  50. ungetc(n, txt);
  51. }
  52.  
  53. // step 5 : 合法数据直接保存
  54. tstr_append(tstr, c);
  55. }
  56.  
  57. fclose(txt);//很重要创建了就要释放,否则会出现隐藏的句柄bug
  58. return tstr;
  59. }

(请原谅, 这种一言不合就上源码的套路.) 主要分5类

  1. "" 字符串, 不处理直接放入

  2. 1-32 空字符直接舍弃, ['!' == 33, 可以查看ascii表]

3. // 注释直接 舍弃到 \n, 行尾

  4. /* 舍弃到 */ 为止

  5. 合法字符直接放入

附注ascii码表如下

挺不错的, 可以自行查查.

同样对于json内存串也是采用相同的处理思路

  1. /*
  2. * 将 jstr中 不需要解析的字符串都去掉,并且纪念mini 比男的还平
  3. * jstr : 待处理的json串
  4. * : 返回压缩后的json串长度
  5. */
  6. static int _cjson_mini(char * jstr) {
  7. char c, *in = jstr, *to = jstr;
  8.  
  9. while ((c = *to)) {
  10. // step 1 : 处理字符串
  11. if (c == '"') {
  12. *in++ = c;
  13. while ((c = *++to) && (c != '"' || to[-] == '\\'))
  14. *in++ = c;
  15. if (c) {
  16. *in++ = c;
  17. ++to;
  18. }
  19. continue;
  20. }
  21.  
  22. // step 2 : 处理不可见特殊字符
  23. if (c < '!') {
  24. ++to;
  25. continue;
  26. }
  27.  
  28. if (c == '/') {
  29. // step 3 : 处理 // 解析到行末尾
  30. if (to[] == '/') {
  31. while ((c = *++to) && c != '\n')
  32. ;
  33. continue;
  34. }
  35.  
  36. // step 4 : 处理 /*
  37. if (to[] == '*') {
  38. while ((c = *++to) && (c != '*' || to[] != '/'))
  39. ;
  40. if (c)
  41. to += ;
  42. continue;
  43. }
  44. }
  45.  
  46. // step 5 : 合法数据直接保存
  47. *in++ = *to++;
  48. }
  49.  
  50. *in = '\0';
  51. return in - jstr;
  52. }

到这里我们就为这个json引擎, 添加上了json注释功能了, 下面会搭建测试环境.

正文  - 测试环境搭建

  跨平台的 scjson引擎[simple c json] , 跨平台的, 采用 Ubuntu linux 搭建一下. window上更好搞. 首先得到所有文件附在下面

schead.h

  1. #ifndef _H_SIMPLEC_SCHEAD
  2. #define _H_SIMPLEC_SCHEAD
  3.  
  4. #include <stdio.h>
  5. #include <errno.h>
  6. #include <string.h>
  7. #include <stdint.h>
  8. #include <stddef.h>
  9. #include <assert.h>
  10. #include <stdbool.h>
  11. #include <scalloc.h>
  12.  
  13. /*
  14. * error => 以后再说
  15. * 跨平台的丑陋从这里开始
  16. * __GNUC => linux 平台特殊操作
  17. * __MSC_VER => window 平台特殊操作
  18. */
  19. #ifdef __GNUC__ // 下面是依赖GCC编译器实现
  20.  
  21. #include <unistd.h>
  22. #include <sys/time.h>
  23. #include <termio.h>
  24.  
  25. // 统一的程序睡眠宏, 单位是毫秒颗粒度
  26. #define SLEEPMS(m) \
  27. usleep(m * )
  28.  
  29. // 屏幕清除宏, 依赖系统脚本
  30. #define CONSOLECLS() \
  31. system("printf '\ec'")
  32.  
  33. /*
  34. * 得到用户输入的一个字符
  35. * : 返回得到字符
  36. */
  37. extern int sh_getch(void);
  38.  
  39. #elif _MSC_VER // 下面是依赖Visual Studio编译器实现
  40.  
  41. #include <Windows.h>
  42. #include <direct.h>
  43. #include <conio.h>
  44.  
  45. // window 删除目录宏
  46. #define rmdir _rmdir
  47.  
  48. // window 上用_getch 替代了getch, 这里为了让其回来
  49. #define sh_getch _getch
  50.  
  51. #define CONSOLECLS() \
  52. system("cls")
  53.  
  54. #define SLEEPMS(m) \
  55. Sleep(m)
  56.  
  57. #else
  58. #error "error : Currently only supports the Visual Studio and GCC!"
  59. #endif
  60.  
  61. /*
  62. * 错误定义枚举 用于判断返回值状态的状态码 RT_*表示返回标志
  63. * 使用举例 :
  64.  
  65. int flag = scconf_get("pursue");
  66. if(flag < RT_SuccessBase) {
  67. sclog_error("get config %s error! flag = %d.", "pursue", flag);
  68. exit(EXIT_FAILURE);
  69. }
  70.  
  71. * 这里是内部 使用的通用返回值 标志. >=0 表示成功, <0 表示失败的情况
  72. */
  73. typedef enum {
  74. RT_SuccessBase = , //结果正确的返回宏
  75. RT_ErrorBase = -, //错误基类型, 所有错误都可用它, 在不清楚的情况下
  76. RT_ErrorParam = -, //调用的参数错误
  77. RT_ErrorMalloc = -, //内存分配错误
  78. RT_ErrorFopen = -, //文件打开失败
  79. RT_ErrorClose = -, //文件描述符读取关闭, 读取完毕也会返回这个
  80. } flag_e;
  81.  
  82. /*
  83. * 定义一些通用的函数指针帮助,主要用于基库的封装中
  84. * 有构造函数, 释放函数, 比较函数等
  85. */
  86. typedef void * (* pnew_f)();
  87. typedef void (* vdel_f)(void * node);
  88. // icmp_f 最好 是 int cmp(const void * ln, const void * rn); 标准结构
  89. typedef int (* icmp_f)();
  90. // 循环操作函数, arg 外部参数, node 内部节点
  91. typedef flag_e (* each_f)(void * node, void * arg);
  92.  
  93. /*
  94. * c 如果是空白字符返回 true, 否则返回false
  95. * c : 必须是 int 值,最好是 char 范围
  96. */
  97. #define sh_isspace(c) \
  98. ((c==' ')||(c>='\t'&&c<='\r'))
  99.  
  100. // 3.0 浮点数据判断宏帮助, __开头表示不希望你使用的宏
  101. #define __DIFF(x, y) ((x)-(y)) //两个表达式做差宏
  102. #define __IF_X(x, z) ((x)<z && (x)>-z) //判断宏,z必须是宏常量
  103. #define EQ(x, y, c) EQ_ZERO(__DIFF(x,y), c) //判断x和y是否在误差范围内相等
  104.  
  105. // 3.1 float判断定义的宏
  106. #define _FLOAT_ZERO (0.000001f) //float 0的误差判断值
  107. #define EQ_FLOAT_ZERO(x) __IF_X(x, _FLOAT_ZERO) //float 判断x是否为零是返回true
  108. #define EQ_FLOAT(x, y) EQ(x, y, _FLOAT_ZERO) //判断表达式x与y是否相等
  109.  
  110. // 3.2 double判断定义的宏
  111. #define _DOUBLE_ZERO (0.000000000001) //double 0误差判断值
  112. #define EQ_DOUBLE_ZERO(x) __IF_X(x, _DOUBLE_ZERO) //double 判断x是否为零是返回true
  113. #define EQ_DOUBLE(x,y) EQ(x, y, _DOUBLE_ZERO) //判断表达式x与y是否相等
  114.  
  115. // 4.0 控制台打印错误信息, fmt必须是双引号括起来的宏
  116. #ifndef CERR
  117. #define CERR(fmt, ...) \
  118. fprintf(stderr,"[%s:%s:%d][error %d:%s]" fmt "\n",\
  119. __FILE__, __func__, __LINE__, errno, strerror(errno), ##__VA_ARGS__)
  120. #endif // !CERR
  121.  
  122. // 4.1 控制台打印错误信息并退出, t同样fmt必须是 ""括起来的字符串常量
  123. #ifndef CERR_EXIT
  124. #define CERR_EXIT(fmt,...) \
  125. CERR(fmt, ##__VA_ARGS__),exit(EXIT_FAILURE)
  126. #endif // !CERR_EXIT
  127.  
  128. // 4.2 执行后检测,如果有错误直接退出
  129. #ifndef IF_CHECK
  130. #define IF_CHECK(code) \
  131. if((code) < ) \
  132. CERR_EXIT(#code)
  133. #endif // !IF_CHECK
  134.  
  135. // 5.0 获取数组长度,只能是数组类型或""字符串常量,后者包含'\0'
  136. #ifndef LEN
  137. #define LEN(arr) \
  138. (sizeof(arr) / sizeof(*(arr)))
  139. #endif/* !ARRLEN */
  140.  
  141. // 7.0 置空操作
  142. #ifndef BZERO
  143. // v必须是个变量
  144. #define BZERO(v) \
  145. memset(&(v), , sizeof(v))
  146. #endif/* !BZERO */
  147.  
  148. // 9.0 scanf 健壮的
  149. #ifndef SAFETY_SCANF
  150. #define _STR_SAFETY_SCANF "Input error, please according to the prompt!"
  151. #define SAFETY_SCANF(scanf_code, ...) \
  152. while(printf(__VA_ARGS__), scanf_code){\
  153. while('\n' != getchar()) \
  154. ;\
  155. puts(_STR_SAFETY_SCANF);\
  156. }\
  157. while('\n' != getchar())
  158. #endif /*!SAFETY_SCANF*/
  159.  
  160. // 简单的time帮助宏
  161. #ifndef TIME_PRINT
  162. #define _STR_TIME_PRINT "The current code block running time:%lf seconds\n"
  163. #define TIME_PRINT(code) \
  164. do{\
  165. clock_t __st, __et;\
  166. __st=clock();\
  167. code\
  168. __et=clock();\
  169. printf(_STR_TIME_PRINT, (0.0 + __et - __st) / CLOCKS_PER_SEC);\
  170. } while()
  171. #endif // !TIME_PRINT
  172.  
  173. /*
  174. * 10.0 这里是一个 在 DEBUG 模式下的测试宏
  175. *
  176. * 用法 :
  177. * DEBUG_CODE({
  178. * puts("debug start...");
  179. * });
  180. */
  181. #ifndef DEBUG_CODE
  182. # ifdef _DEBUG
  183. # define DEBUG_CODE(code) code
  184. # else
  185. # define DEBUG_CODE(code)
  186. # endif // ! _DEBUG
  187. #endif // ! DEBUG_CODE
  188.  
  189. //11.0 等待的宏 是个单线程没有加锁 | "请按任意键继续. . ."
  190. #define _STR_PAUSEMSG "Press any key to continue . . ."
  191. extern void sh_pause(void);
  192. #ifndef INIT_PAUSE
  193.  
  194. # ifdef _DEBUG
  195. # define INIT_PAUSE() atexit(sh_pause)
  196. # else
  197. # define INIT_PAUSE() /* 别说了,都重新开始吧 */
  198. # endif
  199.  
  200. #endif // !INIT_PAUSE
  201.  
  202. //12.0 判断是大端序还是小端序,大端序返回true
  203. extern bool sh_isbig(void);
  204.  
  205. /**
  206. * sh_free - 简单的释放内存函数,对free再封装了一下
  207. **可以避免野指针
  208. **pobj:指向待释放内存的指针(void*)
  209. **/
  210. extern void sh_free(void ** pobj);
  211.  
  212. /*
  213. * 比较两个结构体栈上内容是否相等,相等返回true,不等返回false
  214. * a : 第一个结构体值
  215. * b : 第二个结构体值
  216. * : 相等返回true, 否则false
  217. */
  218. #define STRUCTCMP(a, b) \
  219. (!memcmp(&a, &b, sizeof(a)))
  220.  
  221. #endif// ! _H_SIMPLEC_SCHEAD

schead.c

  1. #include <schead.h>
  2.  
  3. //简单通用的等待函数
  4. inline void
  5. sh_pause(void) {
  6. rewind(stdin);
  7. printf(_STR_PAUSEMSG);
  8. sh_getch();
  9. }
  10.  
  11. //12.0 判断是大端序还是小端序,大端序返回true
  12. inline bool
  13. sh_isbig(void) {
  14. static union {
  15. unsigned short _s;
  16. unsigned char _c;
  17. } __u = { };
  18. return __u._c == ;
  19. }
  20.  
  21. /**
  22. * sh_free - 简单的释放内存函数,对free再封装了一下
  23. **可以避免野指针
  24. **@pobj:指向待释放内存的指针(void*)
  25. **/
  26. void
  27. sh_free(void ** pobj) {
  28. if (pobj == NULL || *pobj == NULL)
  29. return;
  30. free(*pobj);
  31. *pobj = NULL;
  32. }
  33.  
  34. // 为linux扩展一些功能
  35. #if defined(__GNUC__)
  36.  
  37. /*
  38. * 得到用户输入的一个字符
  39. * : 返回得到字符
  40. */
  41. int
  42. sh_getch(void) {
  43. int cr;
  44. struct termios nts, ots;
  45.  
  46. if (tcgetattr(, &ots) < ) // 得到当前终端(0表示标准输入)的设置
  47. return EOF;
  48.  
  49. nts = ots;
  50. cfmakeraw(&nts); // 设置终端为Raw原始模式,该模式下所有的输入数据以字节为单位被处理
  51. if (tcsetattr(, TCSANOW, &nts) < ) // 设置上更改之后的设置
  52. return EOF;
  53.  
  54. cr = getchar();
  55. if (tcsetattr(, TCSANOW, &ots) < ) // 设置还原成老的模式
  56. return EOF;
  57.  
  58. return cr;
  59. }
  60.  
  61. #endif

scalloc.h

  1. #ifndef _H_SIMPLEC_SCALLOC
  2. #define _H_SIMPLEC_SCALLOC
  3.  
  4. #include <stdlib.h>
  5.  
  6. // 释放sm_malloc_和sm_realloc_申请的内存, 必须配套使用
  7. void sm_free_(void * ptr, const char * file, int line, const char * func);
  8. // 返回申请的一段干净的内存
  9. void * sm_malloc_(size_t sz, const char * file, int line, const char * func);
  10. // 返回重新申请的内存, 只能和sm_malloc_配套使用
  11. void * sm_realloc_(void * ptr, size_t sz, const char * file, int line, const char * func);
  12.  
  13. /*
  14. * 释放申请的内存
  15. * ptr : 申请的内存
  16. */
  17. #define sm_free(ptr) sm_free_(ptr, __FILE__, __LINE__, __func__)
  18. /*
  19. * 返回申请的内存, 并且是填充'\0'
  20. * sz : 申请内存的长度
  21. */
  22. #define sm_malloc(sz) sm_malloc_(sz, __FILE__, __LINE__, __func__)
  23. /*
  24. * 返回申请到num*sz长度内存, 并且是填充'\0'
  25. * num : 申请的数量
  26. * sz : 申请内存的长度
  27. */
  28. #define sm_calloc(num, sz) sm_malloc_(num*sz, __FILE__, __LINE__, __func__)
  29. /*
  30. * 返回重新申请的内存
  31. * ptr : 申请的内存
  32. * sz : 申请内存的长度
  33. */
  34. #define sm_realloc(ptr, sz) sm_realloc_(ptr, sz, __FILE__, __LINE__, __func__)
  35.  
  36. // 定义全局内存使用宏, 替换原有的malloc系列函数
  37. #ifndef _SIMPLEC_ALLOC_CLOSE
  38. # define free sm_free
  39. # define malloc sm_malloc
  40. # define calloc sm_calloc
  41. # define realloc sm_realloc
  42. #endif
  43.  
  44. #endif // !_H_SIMPLEC_SCALLOC

scalloc.c

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4.  
  5. // 标识枚举
  6. typedef enum {
  7. HF_Alloc,
  8. HF_Free
  9. } header_e;
  10.  
  11. // 每次申请内存的[16-24]字节额外消耗, 用于记录内存申请情况
  12. struct header {
  13. header_e flag; // 当前内存使用的标识
  14. int line; // 申请的文件行
  15. const char * file; // 申请的文件名
  16. const char * func; // 申请的函数名
  17. };
  18.  
  19. // 内部使用的malloc, 返回内存会用'\0'初始化
  20. void *
  21. sm_malloc_(size_t sz, const char * file, int line, const char * func) {
  22. struct header * ptr = malloc(sz + sizeof(struct header));
  23. // 检查内存分配的结果
  24. if(NULL == ptr) {
  25. fprintf(stderr, "_header_get >%s:%d:%s< alloc error not enough memory start fail!\n", file, line, func);
  26. exit(EXIT_FAILURE);
  27. }
  28.  
  29. ptr->flag = HF_Alloc;
  30. ptr->line = line;
  31. ptr->file = file;
  32. ptr->func = func;
  33. memset(++ptr, , sz);
  34. return ptr;
  35. }
  36.  
  37. // 得到申请内存的开头部分, 并检查
  38. static struct header * _header_get(void * ptr, const char * file, int line, const char * func) {
  39. struct header * node = (struct header *)ptr - ;
  40. // 正常情况直接返回
  41. if(HF_Alloc != node->flag) {
  42. // 异常情况, 内存多次释放, 和内存无效释放
  43. fprintf(stderr, "_header_get free invalid memony flag %d by >%s:%d:%s<\n", node->flag, file, line, func);
  44. exit(EXIT_FAILURE);
  45. }
  46. return node;
  47. }
  48.  
  49. // 内部使用的realloc
  50. void *
  51. sm_realloc_(void * ptr, size_t sz, const char * file, int line, const char * func) {
  52. struct header * node , * buf;
  53. if(NULL == ptr)
  54. return sm_malloc_(sz, file, line, func);
  55.  
  56. // 合理内存分割
  57. node = _header_get(ptr, file, line, func);
  58. node->flag = HF_Free;
  59. // 构造返回内存信息
  60. buf = realloc(node, sz + sizeof(struct header));
  61. buf->flag = HF_Alloc;
  62. buf->line = line;
  63. buf->file = file;
  64. buf->func = func;
  65.  
  66. return buf + ;
  67. }
  68.  
  69. // 内部使用的free, 每次释放都会打印日志信息
  70. void
  71. sm_free_(void * ptr, const char * file, int line, const char * func) {
  72. if(NULL != ptr) {
  73. // 得到内存地址, 并且标识一下, 开始释放
  74. struct header * node = _header_get(ptr, file, line, func);
  75. node->flag = HF_Free;
  76. free(node);
  77. }
  78. }

tstr.h

  1. #ifndef _H_SIMPLEC_TSTR
  2. #define _H_SIMPLEC_TSTR
  3.  
  4. #include <schead.h>
  5.  
  6. //------------------------------------------------简单字符串辅助操作----------------------------------
  7.  
  8. /*
  9. * 主要采用jshash 返回计算后的hash值
  10. * 不冲突率在 80% 左右还可以, 不要传入NULL
  11. */
  12. extern unsigned tstr_hash(const char * str);
  13.  
  14. /*
  15. * 这是个不区分大小写的比较函数
  16. * ls : 左边比较字符串
  17. * rs : 右边比较字符串
  18. * : 返回 ls>rs => >0 ; ls = rs => 0 ; ls<rs => <0
  19. */
  20. extern int tstr_icmp(const char * ls, const char * rs);
  21.  
  22. /*
  23. * 这个代码是 对 strdup 的再实现, 调用之后需要free
  24. * str : 待复制的源码内容
  25. * : 返回 复制后的串内容
  26. */
  27. extern char * tstr_dup(const char * str);
  28.  
  29. //------------------------------------------------简单文本字符串辅助操作----------------------------------
  30.  
  31. #ifndef _STRUCT_TSTR
  32. #define _STRUCT_TSTR
  33. //简单字符串结构,并定义文本字符串类型tstring
  34. struct tstr {
  35. char * str; //字符串实际保存的内容
  36. int len; //当前字符串大小
  37. int size; //字符池大小
  38. };
  39. typedef struct tstr * tstr_t;
  40. #endif // !_STRUCT_TSTR
  41.  
  42. //文本串栈上创建内容,不想用那些技巧了,就这样吧
  43. #define TSTR_NEW(var) \
  44. struct tstr $__##var = { NULL, , }, * var = &$__##var;
  45. #define TSTR_DELETE(var) \
  46. sm_free(var->str)
  47.  
  48. /*
  49. * tstr_t 的创建函数, 会根据str创建一个 tstr_t 结构的字符串
  50. * str : 待创建的字符串
  51. * : 返回创建好的字符串,如果创建失败返回NULL
  52. */
  53. extern tstr_t tstr_new(const char * str);
  54.  
  55. /*
  56. * tstr_t 析构函数
  57. * tstr : tstr_t字符串指针量
  58. */
  59. extern void tstr_delete(tstr_t tstr);
  60.  
  61. /*
  62. * 向简单文本字符串tstr中添加 一个字符c
  63. * tstr : 简单字符串对象
  64. * c : 待添加的字符
  65. */
  66. extern void tstr_append(tstr_t tstr, int c);
  67.  
  68. /*
  69. * 向简单文本串中添加只读字符串
  70. * tstr : 文本串
  71. * str : 待添加的素材串
  72. */
  73. extern void tstr_appends(tstr_t tstr, const char * str);
  74.  
  75. /*
  76. * 复制tstr中内容,得到char *, 需要自己 free释放
  77. * 假如你要清空tstr_t字符串只需要 设置 len = 0.就可以了
  78. * tstr : 待分配的字符串
  79. * : 返回分配好的字符串首地址
  80. */
  81. extern char * tstr_dupstr(tstr_t tstr);
  82.  
  83. //------------------------------------------------简单文件辅助操作----------------------------------
  84.  
  85. /*
  86. * 简单的文件帮助类,会读取完毕这个文件内容返回,失败返回NULL.
  87. * 需要事后使用 tstr_delete(ret); 销毁这个字符串对象
  88. * path : 文件路径
  89. * : 返回创建好的字符串内容,返回NULL表示读取失败
  90. */
  91. extern tstr_t tstr_file_readend(const char * path);
  92.  
  93. /*
  94. * 文件写入,没有好说的, 会返回 RT_SuccessBase | RT_ErrorParam | RT_ErrorFopen
  95. * path : 文件路径
  96. * str : 待写入的字符串
  97. * : 返回操作的结果 见上面枚举
  98. */
  99. extern int tstr_file_writes(const char * path, const char * str);
  100.  
  101. /*
  102. * 文件追加内容 会返回 RT_SuccessBase | RT_ErrorParam | RT_ErrorFopen
  103. * path : 文件路径
  104. * str : 待写入的字符串
  105. * : 返回操作的结果 见上面枚举
  106. */
  107. extern int tstr_file_append(const char * path, const char * str);
  108.  
  109. #endif // !_H_SIMPLEC_TSTR

tstr.c

  1. #include <tstr.h>
  2.  
  3. /*
  4. * 主要采用jshash 返回计算后的hash值
  5. * 不冲突率在 80% 左右还可以, 不要传入NULL
  6. */
  7. unsigned
  8. tstr_hash(const char * str) {
  9. unsigned i, h = (unsigned)strlen(str), sp = (h >> ) + ;
  10. unsigned char * ptr = (unsigned char *)str;
  11.  
  12. for (i = h; i >= sp; i -= sp)
  13. h ^= ((h<<) + (h>>) + ptr[i-]);
  14.  
  15. return h ? h : ;
  16. }
  17.  
  18. /*
  19. * 这是个不区分大小写的比较函数
  20. * ls : 左边比较字符串
  21. * rs : 右边比较字符串
  22. * : 返回 ls>rs => >0 ; ls = rs => 0 ; ls<rs => <0
  23. */
  24. int
  25. tstr_icmp(const char * ls, const char * rs) {
  26. int l, r;
  27. if(!ls || !rs)
  28. return (int)(ls - rs);
  29.  
  30. do {
  31. if((l=*ls++)>='a' && l<='z')
  32. l -= 'a' - 'A';
  33. if((r=*rs++)>='a' && r<='z')
  34. r -= 'a' - 'A';
  35. } while(l && l==r);
  36.  
  37. return l - r;
  38. }
  39.  
  40. /*
  41. * 这个代码是 对 strdup 的再实现, 调用之后需要free
  42. * str : 待复制的源码内容
  43. * : 返回 复制后的串内容
  44. */
  45. char *
  46. tstr_dup(const char * str)
  47. {
  48. size_t len;
  49. char * nstr;
  50. if (NULL == str)
  51. return NULL;
  52.  
  53. len = sizeof(char) * (strlen(str) + );
  54. nstr = sm_malloc(len);
  55. // 返回最后结果
  56. return memcpy(nstr, str, len);
  57. }
  58.  
  59. //------------------------------------------------简单文本字符串辅助操作----------------------------------
  60.  
  61. /*
  62. * tstr_t 的创建函数, 会根据str创建一个 tstr_t 结构的字符串
  63. * str : 待创建的字符串
  64. * : 返回创建好的字符串,如果创建失败返回NULL
  65. */
  66. tstr_t
  67. tstr_new(const char * str) {
  68. tstr_t tstr = sm_malloc(sizeof(struct tstr));
  69. tstr_appends(tstr, str);
  70. return tstr;
  71. }
  72.  
  73. /*
  74. * tstr_t 析构函数
  75. * tstr : tstr_t字符串指针量
  76. */
  77. inline void
  78. tstr_delete(tstr_t tstr) {
  79. if (tstr) {
  80. sm_free(tstr->str);
  81. sm_free(tstr);
  82. }
  83. }
  84.  
  85. //文本字符串创建的度量值
  86. #define _INT_TSTRING (32)
  87.  
  88. //简单分配函数,智力一定会分配内存的, len > size的时候调用这个函数
  89. static void _tstr_realloc(tstr_t tstr, int len)
  90. {
  91. int size = tstr->size;
  92. for (size = size < _INT_TSTRING ? _INT_TSTRING : size; size < len; size <<= )
  93. ;
  94. //分配内存
  95. tstr->str = sm_realloc(tstr->str, size);
  96. tstr->size = size;
  97. }
  98.  
  99. /*
  100. * 向简单文本字符串tstr中添加 一个字符c
  101. * tstr : 简单字符串对象
  102. * c : 待添加的字符
  103. */
  104. void
  105. tstr_append(tstr_t tstr, int c) {
  106. //不做安全检查
  107. int len = tstr->len + + ; // c + '\0' 而len只指向 字符串strlen长度
  108.  
  109. //需要的❀, 需要进行内存分配, 唯一损失
  110. if (len > tstr->size)
  111. _tstr_realloc(tstr, len);
  112.  
  113. tstr->len = --len;
  114. tstr->str[len - ] = c;
  115. tstr->str[len] = '\0';
  116. }
  117.  
  118. /*
  119. * 向简单文本串中添加只读字符串
  120. * tstr : 文本串
  121. * str : 待添加的素材串
  122. * : 返回状态码主要是 _RT_EP _RT_EM
  123. */
  124. void
  125. tstr_appends(tstr_t tstr, const char * str) {
  126. int len;
  127. if (!tstr || !str || !*str)
  128. return;
  129.  
  130. // 检查内存是否需要重新构建
  131. len = tstr->len + (int)strlen(str) + ;
  132. if (len > tstr->size)
  133. _tstr_realloc(tstr, len);
  134.  
  135. strcpy(tstr->str + tstr->len, str);
  136. tstr->len = len - ;
  137. }
  138.  
  139. /*
  140. * 复制tstr中内容,得到char *, 需要自己 free释放
  141. * 假如你要清空tstr_t字符串只需要 设置 len = 0.就可以了
  142. * tstr : 待分配的字符串
  143. * : 返回分配好的字符串首地址
  144. */
  145. char *
  146. tstr_dupstr(tstr_t tstr) {
  147. char * str;
  148. if (!tstr || tstr->len <= )
  149. return NULL;
  150.  
  151. //下面就可以复制了,采用最快的一种方式
  152. str = sm_malloc(tstr->len + );
  153. return memcpy(str, tstr->str, tstr->len + );
  154. }
  155.  
  156. //------------------------------------------------简单文件辅助操作----------------------------------
  157.  
  158. /*
  159. * 简单的文件帮助类,会读取完毕这个文件内容返回,失败返回NULL.
  160. * 需要事后使用 tstr_delete(ret); 销毁这个字符串对象
  161. * path : 文件路径
  162. * : 返回创建好的字符串内容,返回NULL表示读取失败
  163. */
  164. tstr_t
  165. tstr_file_readend(const char * path) {
  166. int c;
  167. tstr_t tstr;
  168. FILE * txt = fopen(path, "r");
  169. if (NULL == txt) {
  170. CERR("fopen r %s is error!", path);
  171. return NULL;
  172. }
  173.  
  174. //这里创建文本串对象
  175. tstr = tstr_new(NULL);
  176.  
  177. //这里读取文本内容
  178. while ((c = fgetc(txt)) != EOF)
  179. tstr_append(tstr, c);
  180.  
  181. fclose(txt);//很重要创建了就要释放,否则会出现隐藏的句柄bug
  182. return tstr;
  183. }
  184.  
  185. int _tstr_file_writes(const char * path, const char * str, const char * mode) {
  186. FILE* txt;
  187. // 检查参数是否有问题
  188. if (!path || !*path || !str) {
  189. CERR("check is '!path || !*path || !str'");
  190. return RT_ErrorParam;
  191. }
  192.  
  193. if ((txt = fopen(path, mode)) == NULL) {
  194. CERR("fopen mode = '%s', path = '%s' error!", mode, path);
  195. return RT_ErrorFopen;
  196. }
  197.  
  198. //这里写入信息
  199. fputs(str, txt);
  200.  
  201. fclose(txt);
  202. return RT_SuccessBase;
  203. }
  204.  
  205. /*
  206. * 文件写入,没有好说的, 会返回 RT_SuccessBase | RT_ErrorParam | RT_ErrorFopen
  207. * path : 文件路径
  208. * str : 待写入的字符串
  209. * : 返回操作的结果 见上面枚举
  210. */
  211. inline int
  212. tstr_file_writes(const char * path, const char * str) {
  213. return _tstr_file_writes(path, str, "wb");
  214. }
  215.  
  216. /*
  217. * 文件追加内容 会返回 RT_SuccessBase | RT_ErrorParam | RT_ErrorFopen
  218. * path : 文件路径
  219. * str : 待写入的字符串
  220. * : 返回操作的结果 见上面枚举
  221. */
  222. inline int
  223. tstr_file_appends(const char * path, const char * str) {
  224. return _tstr_file_writes(path, str, "ab");
  225. }

scjson.h

  1. #ifndef _H_SIMPLEC_SCJSON
  2. #define _H_SIMPLEC_SCJSON
  3.  
  4. #include <tstr.h>
  5.  
  6. // json 中几种数据类型定义 , 对于C而言 最难的是看不见源码,而不是api复杂, 更不是业务复杂
  7. #define _CJSON_FALSE (0)
  8. #define _CJSON_TRUE (1)
  9. #define _CJSON_NULL (2)
  10. #define _CJSON_NUMBER (3)
  11. #define _CJSON_STRING (4)
  12. #define _CJSON_ARRAY (5)
  13. #define _CJSON_OBJECT (6)
  14.  
  15. #define _CJSON_ISREF (256) //set 时候用如果是引用就不释放了
  16. #define _CJSON_ISCONST (512) //set时候用, 如果是const char* 就不释放了
  17.  
  18. struct cjson {
  19. struct cjson * next, * prev;
  20. struct cjson * child; // type == _CJSON_ARRAY or type == _CJSON_OBJECT 那么 child 就不为空
  21.  
  22. int type;
  23. char * key; // json内容那块的 key名称
  24. char * vs; // type == _CJSON_STRING, 是一个字符串
  25. double vd; // type == _CJSON_NUMBER, 是一个num值, ((int)c->vd) 转成int 或 bool
  26. };
  27.  
  28. //定义cjson_t json类型
  29. typedef struct cjson * cjson_t;
  30.  
  31. /*
  32. * 这个宏,协助我们得到 int 值 或 bool 值
  33. *
  34. * item : 待处理的目标cjson_t结点
  35. */
  36. #define cjson_getint(item) \
  37. ((int)((item)->vd))
  38.  
  39. /*
  40. * 删除json串内容
  41. * c : 待释放json_t串内容
  42. */
  43. extern void cjson_delete(cjson_t c);
  44.  
  45. /*
  46. * 对json字符串解析返回解析后的结果
  47. * jstr : 待解析的字符串
  48. */
  49. extern cjson_t cjson_newtstr(tstr_t str);
  50.  
  51. /*
  52. * 将json文件解析成json内容返回. 需要自己调用 cjson_delete
  53. * path : json串路径
  54. * : 返回处理好的cjson_t 内容,失败返回NULL
  55. */
  56. extern cjson_t cjson_newfile(const char * path);
  57.  
  58. /*
  59. * 根据 item当前结点的 next 一直寻找到 NULL, 返回个数. 推荐在数组的时候使用
  60. * array : 待处理的cjson_t数组对象
  61. * : 返回这个数组中长度
  62. */
  63. extern int cjson_getlen(cjson_t array);
  64.  
  65. /*
  66. * 根据索引得到这个数组中对象
  67. * array : 数组对象
  68. * idx : 查找的索引 必须 [0,cjson_getlen(array)) 范围内
  69. * : 返回查找到的当前对象
  70. */
  71. extern cjson_t cjson_getarray(cjson_t array, int idx);
  72.  
  73. /*
  74. * 根据key得到这个对象 相应位置的值
  75. * object : 待处理对象中值
  76. * key : 寻找的key
  77. * : 返回 查找 cjson_t 对象
  78. */
  79. extern cjson_t cjson_getobject(cjson_t object, const char * key);
  80.  
  81. // --------------------------------- 下面是 cjson 输出部分的处理代码 -----------------------------------------
  82.  
  83. /*
  84. * 这里是将 cjson_t item 转换成字符串内容,需要自己free
  85. * item : cjson的具体结点
  86. * : 返回生成的item的json串内容
  87. */
  88. extern char* cjson_print(cjson_t item);
  89.  
  90. // --------------------------------- 下面是 cjson 输出部分的辅助代码 -----------------------------------------
  91.  
  92. /*
  93. * 创建一个bool的对象 b==0表示false,否则都是true, 需要自己释放 cjson_delete
  94. * b : bool 值 最好是 _Bool
  95. * : 返回 创建好的json 内容
  96. */
  97. extern cjson_t cjson_newnull();
  98. extern cjson_t cjson_newbool(int b);
  99. extern cjson_t cjson_newnumber(double vd);
  100. extern cjson_t cjson_newstring(const char * vs);
  101. extern cjson_t cjson_newarray(void);
  102. extern cjson_t cjson_newobject(void);
  103.  
  104. /*
  105. * 按照类型,创建 对映类型的数组 cjson对象
  106. *目前支持 _CJSON_NULL _CJSON_BOOL/FALSE or TRUE , _CJSON_NUMBER, _CJSON_STRING
  107. * NULL => array 传入NULL, FALSE 使用char[],也可以传入NULL, NUMBER 只接受double, string 只接受char**
  108. * type : 类型目前支持 上面几种类型
  109. * array : 数组原始数据
  110. * len : 数组中元素长度
  111. * : 返回创建的数组对象
  112. */
  113. extern cjson_t cjson_newtypearray(int type, const void * array, int len);
  114.  
  115. /*
  116. * 在array中分离第idx个索引项内容.
  117. * array : 待处理的json_t 数组内容
  118. * idx : 索引内容
  119. * : 返回分离的json_t内容
  120. */
  121. extern cjson_t cjson_detacharray(cjson_t array, int idx);
  122.  
  123. /*
  124. * 在object json 中分离 key 的项出去
  125. * object : 待分离的对象主体内容
  126. * key : 关联的键
  127. * : 返回分离的 object中 key的项json_t
  128. */
  129. extern cjson_t cjson_detachobject(cjson_t object, const char * key);
  130.  
  131. #endif // !_H_SIMPLEC_SCJSON

scjson.c

  1. #include <scjson.h>
  2. #include <float.h>
  3. #include <limits.h>
  4. #include <math.h>
  5.  
  6. // 删除cjson
  7. static void _cjson_delete(cjson_t c) {
  8. cjson_t next;
  9. while (c) {
  10. next = c->next;
  11. //递归删除儿子
  12. if (!(c->type & _CJSON_ISREF)) {
  13. if (c->child) //如果不是尾递归,那就先递归
  14. _cjson_delete(c->child);
  15. if (c->vs)
  16. sm_free(c->vs);
  17. }
  18. else if (!(c->type & _CJSON_ISCONST) && c->key)
  19. sm_free(c->key);
  20. sm_free(c);
  21. c = next;
  22. }
  23. }
  24.  
  25. /*
  26. * 删除json串内容,最近老是受清华的老学生打击, 会起来的......
  27. * c : 待释放json_t串内容
  28. */
  29. inline void
  30. cjson_delete(cjson_t c) {
  31. if (NULL == c)
  32. return;
  33. _cjson_delete(c);
  34. }
  35.  
  36. //构造一个空 cjson 对象
  37. static inline cjson_t _cjson_new(void) {
  38. return sm_malloc(sizeof(struct cjson));
  39. }
  40.  
  41. // 简化的代码段,用宏来简化代码书写 , 16进制处理
  42. #define __parse_hex4_code(c, h) \
  43. if (c >= '' && c <= '') \
  44. h += c - ''; \
  45. else if (c >= 'A' && c <= 'F') \
  46. h += + c - 'A'; \
  47. else if (c >= 'a' && c <= 'z') \
  48. h += + c - 'F'; \
  49. else \
  50. return
  51.  
  52. // 等到unicode char代码
  53. static unsigned _parse_hex4(const char * str) {
  54. unsigned h = ;
  55. char c = *str;
  56. //第一轮
  57. __parse_hex4_code(c, h);
  58. h <<= ;
  59. c = *++str;
  60. //第二轮
  61. __parse_hex4_code(c, h);
  62. h <<= ;
  63. c = *++str;
  64. //第三轮
  65. __parse_hex4_code(c, h);
  66. h <<= ;
  67. c = *++str;
  68. //第四轮
  69. __parse_hex4_code(c, h);
  70.  
  71. return h;
  72. }
  73.  
  74. // 分析字符串的子函数,
  75. static const char* _parse_string(cjson_t item, const char * str) {
  76. static unsigned char _marks[] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
  77. const char * ptr;
  78. char * nptr, * out;
  79. char c;
  80. int len;
  81. unsigned uc, nuc;
  82.  
  83. if (*str != '\"') { // 检查是否是字符串内容
  84. CERR("need \\\" str => %s error!", str);
  85. return NULL;
  86. }
  87.  
  88. for (ptr = str + , len = ; (c = *ptr++) != '\"' && c; ++len)
  89. if (c == '\\') //跳过转义字符
  90. ++ptr;
  91. out = sm_malloc(len + );
  92. // 这里复制拷贝内容
  93. for (ptr = str + , nptr = out; (c = *ptr) != '\"' && c; ++ptr) {
  94. if (c != '\\') {
  95. *nptr++ = c;
  96. continue;
  97. }
  98. // 处理转义字符
  99. switch ((c = *++ptr)) {
  100. case 'b': *nptr++ = '\b'; break;
  101. case 'f': *nptr++ = '\f'; break;
  102. case 'n': *nptr++ = '\n'; break;
  103. case 'r': *nptr++ = '\r'; break;
  104. case 't': *nptr++ = '\t'; break;
  105. case 'u': // 将utf16 => utf8, 专门的utf处理代码
  106. uc = _parse_hex4(ptr + );
  107. ptr += ;//跳过后面四个字符, unicode
  108. if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == ) break; /* check for invalid. */
  109.  
  110. if (uc >= 0xD800 && uc <= 0xDBFF) { /* UTF16 surrogate pairs. */
  111. if (ptr[] != '\\' || ptr[] != 'u')
  112. break; /* missing second-half of surrogate. */
  113. nuc = _parse_hex4(ptr + );
  114. ptr += ;
  115. if (nuc < 0xDC00 || nuc>0xDFFF)
  116. break; /* invalid second-half of surrogate. */
  117. uc = 0x10000 + (((uc & 0x3FF) << ) | (nuc & 0x3FF));
  118. }
  119.  
  120. len = ;
  121. if (uc < 0x80)
  122. len = ;
  123. else if (uc < 0x800)
  124. len = ;
  125. else if (uc < 0x10000)
  126. len = ;
  127. nptr += len;
  128.  
  129. switch (len) {
  130. case : *--nptr = ((uc | 0x80) & 0xBF); uc >>= ;
  131. case : *--nptr = ((uc | 0x80) & 0xBF); uc >>= ;
  132. case : *--nptr = ((uc | 0x80) & 0xBF); uc >>= ;
  133. case : *--nptr = (uc | _marks[len]);
  134. }
  135. nptr += len;
  136. break;
  137. default: *nptr++ = c;
  138. }
  139. }
  140.  
  141. *nptr = '\0';
  142. if (c == '\"')
  143. ++ptr;
  144. item->vs = out;
  145. item->type = _CJSON_STRING;
  146. return ptr;
  147. }
  148.  
  149. // 分析数值的子函数,写的可以
  150. static const char * _parse_number(cjson_t item, const char * str) {
  151. double n = 0.0, ns = 1.0, nd = 0.0; //n把偶才能值, ns表示开始正负, 负为-1, nd 表示小数后面位数
  152. int e = , es = ; //e表示后面指数, es表示 指数的正负,负为-1
  153. char c;
  154.  
  155. if ((c = *str) == '-' || c == '+') {
  156. ns = c == '-' ? -1.0 : 1.0; //正负号检测, 1表示负数
  157. ++str;
  158. }
  159. //处理整数部分
  160. for (c = *str; c >= '' && c <= ''; c = *++str)
  161. n = n * + c - '';
  162. if (c == '.')
  163. for (; (c = *++str) >= '' && c <= ''; --nd)
  164. n = n * + c - '';
  165.  
  166. // 处理科学计数法
  167. if (c == 'e' || c == 'E') {
  168. if ((c = *++str) == '+') //处理指数部分
  169. ++str;
  170. else if (c == '-')
  171. es = -, ++str;
  172. for (; (c = *str) >= '' && c <= ''; ++str)
  173. e = e * + c - '';
  174. }
  175.  
  176. //返回最终结果 number = +/- number.fraction * 10^+/- exponent
  177. n = ns * n * pow(10.0, nd + es * e);
  178. item->vd = n;
  179. item->type = _CJSON_NUMBER;
  180. return str;
  181. }
  182.  
  183. // 递归下降分析 需要声明这些函数
  184. static const char * _parse_array(cjson_t item, const char * str);
  185. static const char * _parse_object(cjson_t item, const char * str);
  186. static const char * _parse_value(cjson_t item, const char * value);
  187.  
  188. // 分析数组的子函数, 采用递归下降分析
  189. static const char * _parse_array(cjson_t item, const char * str) {
  190. cjson_t child;
  191. if (*str != '[') {
  192. CERR("array str error start: %s.", str);
  193. return NULL;
  194. }
  195.  
  196. item->type = _CJSON_ARRAY;
  197. str = str + ;
  198. if (*str == ']') // 低估提前结束
  199. return str + ;
  200.  
  201. item->child = child = _cjson_new();
  202. str = _parse_value(child, str);
  203. if (NULL == str) { // 解析失败 直接返回
  204. CERR("array str error e n d one: %s.", str);
  205. return NULL;
  206. }
  207. while (*str == ',') {
  208. cjson_t nitem = _cjson_new();
  209. child->next = nitem;
  210. nitem->prev = child;
  211. child = nitem;
  212. str = _parse_value(child, str + );
  213. if (NULL == str) {// 写代码是一件很爽的事
  214. CERR("array str error e n d two: %s.", str);
  215. return NULL;
  216. }
  217. }
  218.  
  219. if (*str != ']') {
  220. CERR("array str error e n d: %s.", str);
  221. return NULL;
  222. }
  223. return str + ; // 跳过']'
  224. }
  225.  
  226. // 分析对象的子函数
  227. static const char * _parse_object(cjson_t item, const char * str) {
  228. cjson_t child;
  229. if (*str != '{') {
  230. CERR("object str error start: %s.", str);
  231. return NULL;
  232. }
  233.  
  234. item->type = _CJSON_OBJECT;
  235. str = str + ;
  236. if (*str == '}')
  237. return str + ;
  238.  
  239. //处理结点, 开始读取一个 key
  240. item->child = child = _cjson_new();
  241. str = _parse_string(child, str);
  242. if (!str || *str != ':') {
  243. CERR("_skip _parse_string is error : %s!", str);
  244. return NULL;
  245. }
  246. child->key = child->vs;
  247. child->vs = NULL;
  248.  
  249. str = _parse_value(child, str + );
  250. if (!str) {
  251. CERR("_parse_object _parse_value is error 2!");
  252. return NULL;
  253. }
  254.  
  255. // 递归解析
  256. while (*str == ',') {
  257. cjson_t nitem = _cjson_new();
  258. child->next = nitem;
  259. nitem->prev = child;
  260. child = nitem;
  261. str = _parse_string(child, str + );
  262. if (!str || *str != ':'){
  263. CERR("_parse_string need name or no equal ':' %s.", str);
  264. return NULL;
  265. }
  266. child->key = child->vs;
  267. child->vs = NULL;
  268.  
  269. str = _parse_value(child, str+);
  270. if (!str) {
  271. CERR("_parse_string need item two ':' %s.", str);
  272. return NULL;
  273. }
  274. }
  275.  
  276. if (*str != '}') {
  277. CERR("object str error e n d: %s.", str);
  278. return NULL;
  279. }
  280. return str + ;
  281. }
  282.  
  283. // 将value 转换塞入 item json值中一部分
  284. static const char * _parse_value(cjson_t item, const char * value) {
  285. char c;
  286. if ((value) && (c = *value)) {
  287. switch (c) {
  288. // n = null, f = false, t = true
  289. case 'n' : return item->type = _CJSON_NULL, value + ;
  290. case 'f' : return item->type = _CJSON_FALSE, value + ;
  291. case 't' : return item->type = _CJSON_TRUE, item->vd = 1.0, value + ;
  292. case '\"': return _parse_string(item, value);
  293. case '' : case '': case '': case '': case '': case '': case '': case '': case '': case '':
  294. case '+' : case '-': return _parse_number(item, value);
  295. case '[' : return _parse_array(item, value);
  296. case '{' : return _parse_object(item, value);
  297. }
  298. }
  299. // 循环到这里是意外 数据
  300. CERR("params value = %s!", value);
  301. return NULL;
  302. }
  303.  
  304. /*
  305. * 对json字符串解析返回解析后的结果
  306. * jstr : 待解析的字符串
  307. * : 返回解析好的字符串内容
  308. */
  309. static cjson_t _cjson_parse(const char * jstr) {
  310. cjson_t c = _cjson_new();
  311. const char * end;
  312.  
  313. if (!(end = _parse_value(c, jstr))) {
  314. CERR("_parse_value params end = %s!", end);
  315. cjson_delete(c);
  316. return NULL;
  317. }
  318.  
  319. //这里是否检测 返回测试数据
  320. return c;
  321. }
  322.  
  323. /*
  324. * 将 jstr中 不需要解析的字符串都去掉,并且纪念mini 比男的还平
  325. * jstr : 待处理的json串
  326. * : 返回压缩后的json串长度
  327. */
  328. static int _cjson_mini(char * jstr) {
  329. char c, *in = jstr, *to = jstr;
  330.  
  331. while ((c = *to)) {
  332. // step 1 : 处理字符串
  333. if (c == '"') {
  334. *in++ = c;
  335. while ((c = *++to) && (c != '"' || to[-] == '\\'))
  336. *in++ = c;
  337. if (c) {
  338. *in++ = c;
  339. ++to;
  340. }
  341. continue;
  342. }
  343.  
  344. // step 2 : 处理不可见特殊字符
  345. if (c < '!') {
  346. ++to;
  347. continue;
  348. }
  349.  
  350. if (c == '/') {
  351. // step 3 : 处理 // 解析到行末尾
  352. if (to[] == '/') {
  353. while ((c = *++to) && c != '\n')
  354. ;
  355. continue;
  356. }
  357.  
  358. // step 4 : 处理 /*
  359. if (to[] == '*') {
  360. while ((c = *++to) && (c != '*' || to[] != '/'))
  361. ;
  362. if (c)
  363. to += ;
  364. continue;
  365. }
  366. }
  367.  
  368. // step 5 : 合法数据直接保存
  369. *in++ = *to++;
  370. }
  371.  
  372. *in = '\0';
  373. return in - jstr;
  374. }
  375.  
  376. /*
  377. * 对json字符串解析返回解析后的结果
  378. * jstr : 待解析的字符串
  379. */
  380. inline cjson_t
  381. cjson_newtstr(tstr_t str) {
  382. str->len = _cjson_mini(str->str);
  383. return _cjson_parse(str->str);
  384. }
  385.  
  386. // 从json文件中解析出最简json数据
  387. static tstr_t _cjson_newfile(const char * path) {
  388. char c, n;
  389. tstr_t tstr;
  390. FILE * txt = fopen(path, "r");
  391. if (NULL == txt) {
  392. CERR("fopen r %s is error!", path);
  393. return NULL;
  394. }
  395.  
  396. //这里创建文本串对象
  397. tstr = tstr_new(NULL);
  398.  
  399. while ((c = fgetc(txt)) != EOF) {
  400. // step 1 : 处理字符串
  401. if (c == '"') {
  402. tstr_append(tstr, c);
  403. for (n = c; ((c = fgetc(txt)) != EOF) && (c != '"' || n == '\\'); n = c)
  404. tstr_append(tstr, c);
  405. if (EOF != c)
  406. tstr_append(tstr, c);
  407. continue;
  408. }
  409.  
  410. // step 2 : 处理不可见特殊字符
  411. if (c < '!')
  412. continue;
  413.  
  414. if (c == '/') {
  415. // step 3 : 处理 // 解析到行末尾
  416. n = fgetc(txt);
  417. if (n == '/') {
  418. while ((c = fgetc(txt)) != EOF && c != '\n')
  419. ;
  420. continue;
  421. }
  422.  
  423. // step 4 : 处理 /*
  424. if (n == '*') {
  425. while ((c = fgetc(txt)) != EOF) {
  426. if (c == '*') {
  427. n = fgetc(txt);
  428. if (n == '/')
  429. break;
  430. ungetc(n, txt);
  431. }
  432. }
  433. continue;
  434. }
  435. ungetc(n, txt);
  436. }
  437.  
  438. // step 5 : 合法数据直接保存
  439. tstr_append(tstr, c);
  440. }
  441.  
  442. fclose(txt);//很重要创建了就要释放,否则会出现隐藏的句柄bug
  443. return tstr;
  444. }
  445.  
  446. /*
  447. * 将json文件解析成json内容返回. 需要自己调用 cjson_delete
  448. * path : json串路径
  449. * : 返回处理好的cjson_t 内容,失败返回NULL
  450. */
  451. cjson_t
  452. cjson_newfile(const char * path) {
  453. cjson_t root;
  454. tstr_t tstr = _cjson_newfile(path);
  455. if (!tstr) {
  456. CERR("_cjson_dofile_tstr path:%s is error!", path);
  457. return NULL;
  458. }
  459. root = _cjson_parse(tstr->str);
  460. tstr_delete(tstr);
  461. return root;
  462. }
  463.  
  464. /*
  465. * 根据 item当前结点的 next 一直寻找到 NULL, 返回个数. 推荐在数组的时候使用
  466. * array : 待处理的cjson_t数组对象
  467. * : 返回这个数组中长度
  468. */
  469. int
  470. cjson_getlen(cjson_t array) {
  471. int len = ;
  472. if (array) {
  473. for (array = array->child; array; array = array->next)
  474. ++len;
  475. }
  476. return len;
  477. }
  478.  
  479. /*
  480. * 根据索引得到这个数组中对象
  481. * array : 数组对象
  482. * idx : 查找的索引 必须 [0,cjson_getlen(array)) 范围内
  483. * : 返回查找到的当前对象
  484. */
  485. cjson_t
  486. cjson_getarray(cjson_t array, int idx) {
  487. cjson_t c;
  488. DEBUG_CODE({
  489. if (!array || idx < ) {
  490. CERR("array:%p, idx=%d params is error!", array, idx);
  491. return NULL;
  492. }
  493. });
  494.  
  495. for (c = array->child; c&&idx > ; c = c->next)
  496. --idx;
  497.  
  498. return c;
  499. }
  500.  
  501. /*
  502. * 根据key得到这个对象 相应位置的值
  503. * object : 待处理对象中值
  504. * key : 寻找的key
  505. * : 返回 查找 cjson_t 对象
  506. */
  507. cjson_t
  508. cjson_getobject(cjson_t object, const char* key) {
  509. cjson_t c;
  510. DEBUG_CODE({
  511. if (!object || !key || !*key) {
  512. CERR("object:%p, key=%s params is error!", object, key);
  513. return NULL;
  514. }
  515. });
  516.  
  517. for (c = object->child; c && tstr_icmp(key, c->key); c = c->next)
  518. ;
  519.  
  520. return c;
  521. }
  522.  
  523. // --------------------------------- 下面是 cjson 输出部分的处理代码 -----------------------------------------
  524.  
  525. // 2^n>=x , n是最小的整数
  526. static int _pow2gt(int x) {
  527. --x;
  528. x |= x >> ;
  529. x |= x >> ;
  530. x |= x >> ;
  531. x |= x >> ;
  532. x |= x >> ;
  533. return x + ;
  534. }
  535.  
  536. /*
  537. * 这里使用 tstr_t 结构 size 这里表示 字符串总大小,没有变化
  538. * len 表示当前字符串的字符串起始偏移量 即 tstr_t->str + tstr_t->len 起始的
  539. */
  540. static char* _ensure(tstr_t p, int need) {
  541. char * nbuf;
  542. int nsize;
  543. if (!p || !p->str) {
  544. CERR("p:%p need:%d is error!", p, need);
  545. return NULL;
  546. }
  547. need += p->len;
  548. if (need <= p->size) //内存够用直接返回结果
  549. return p->str + p->len;
  550. nsize = _pow2gt(need);
  551. // 一定会成功, 否则一切都回归奇点
  552. nbuf = sm_malloc(nsize * sizeof(char));
  553. //这里复制内容
  554. memcpy(nbuf, p->str, p->size);
  555. sm_free(p->str);
  556. p->size = nsize;
  557. p->str = nbuf;
  558. return nbuf + p->len;
  559. }
  560.  
  561. // 这里更新一下 当前字符串, 返回当前字符串的长度
  562. static inline int _update(tstr_t p) {
  563. return (!p || !p->str) ? : p->len + (int)strlen(p->str+p->len);
  564. }
  565.  
  566. // 将item 中值转换成字符串 保存到p中
  567. static char * _print_number(cjson_t item, tstr_t p) {
  568. char* str = NULL;
  569. double d = item->vd;
  570. int i = (int)d;
  571.  
  572. if (d == ) { //普通0
  573. str = _ensure(p, );
  574. if (str)
  575. str[] = '', str[] = '\0';
  576. }
  577. else if ((fabs(d - i)) <= DBL_EPSILON && d <= INT_MAX && d >= INT_MIN) {
  578. str = _ensure(p, ); //int 值
  579. if (str)
  580. sprintf(str, "%d", i);
  581. }
  582. else {
  583. str = _ensure(p, ); //double值
  584. if (str) {
  585. double nd = fabs(d); //得到正值开始比较
  586. if(fabs(floor(d) - d) <= DBL_EPSILON && nd < 1.0e60)
  587. sprintf(str, "%.0f", d);
  588. else if(nd < 1.0e-6 || nd > 1.0e9) //科学计数法
  589. sprintf(str, "%e", d);
  590. else
  591. sprintf(str, "%f", d);
  592.  
  593. }
  594. }
  595.  
  596. return str;
  597. }
  598.  
  599. // 输出字符串内容
  600. static char * _print_string(char * str, tstr_t p) {
  601. const char * ptr;
  602. char * nptr, * out;
  603. int len = , flag = ;
  604. unsigned char c;
  605.  
  606. if (!str || !*str) { //最特殊情况,什么都没有 返回NULL
  607. out = _ensure(p, );
  608. if (!out)
  609. return NULL;
  610. out[] = '\"', out[] = '\"', out[] = '\0';
  611. return out;
  612. }
  613.  
  614. for (ptr = str; (c=*ptr); ++ptr)
  615. flag |= ((c > && c < ) || c == '\"' || c == '\\');
  616.  
  617. if (!flag) { //没有特殊字符直接处理结果
  618. len = (int)(ptr - str);
  619. out = _ensure(p,len + );
  620. if (!out)
  621. return NULL;
  622. nptr = out;
  623. *nptr++ = '\"';
  624. strcpy(nptr, str);
  625. nptr[len] = '\"';
  626. nptr[len + ] = '\0';
  627. return out;
  628. }
  629.  
  630. //处理 存在 "和转义字符内容
  631. for (ptr = str; (c = *ptr) && ++len; ++ptr) {
  632. if (strchr("\"\\\b\f\n\r\t", c))
  633. ++len;
  634. else if (c < ) //隐藏字符的处理, 这里可以改
  635. len += ;
  636. }
  637.  
  638. if ((out = _ensure(p, len + )) == NULL)
  639. return NULL;
  640. //先添加 \"
  641. nptr = out;
  642. *nptr++ = '\"';
  643. for (ptr = str; (c = *ptr); ++ptr) {
  644. if (c > && c != '\"' && c != '\\') {
  645. *nptr++ = c;
  646. continue;
  647. }
  648. *nptr++ = '\\';
  649. switch (c){
  650. case '\\': *nptr++ = '\\'; break;
  651. case '\"': *nptr++ = '\"'; break;
  652. case '\b': *nptr++ = 'b'; break;
  653. case '\f': *nptr++ = 'f'; break;
  654. case '\n': *nptr++ = 'n'; break;
  655. case '\r': *nptr++ = 'r'; break;
  656. case '\t': *nptr++ = 't'; break;
  657. default: sprintf(nptr, "u%04x", c);nptr += ; /* 不可见字符 采用 4字节字符编码 */
  658. }
  659. }
  660. *nptr++ = '\"';
  661. *nptr = '\0';
  662. return out;
  663. }
  664.  
  665. //这里是 递归下降 的函数声明处, 分别是处理值, 数组, object
  666. static char * _print_value(cjson_t item, tstr_t p);
  667. static char * _print_array(cjson_t item, tstr_t p);
  668. static char * _print_object(cjson_t item, tstr_t p);
  669.  
  670. // 定义实现部分, 内部私有函数 认为 item 和 p都是存在的
  671. static char * _print_value(cjson_t item, tstr_t p) {
  672. char * out = NULL;
  673. switch ((item->type) & UCHAR_MAX) { // 0xff
  674. case _CJSON_FALSE: if ((out = _ensure(p, ))) strcpy(out, "false"); break;
  675. case _CJSON_TRUE: if ((out = _ensure(p, ))) strcpy(out, "true"); break;
  676. case _CJSON_NULL: if ((out = _ensure(p, ))) strcpy(out, "null"); break;
  677. case _CJSON_NUMBER: out = _print_number(item, p); break;
  678. case _CJSON_STRING: out = _print_string(item->vs, p); break;
  679. case _CJSON_ARRAY: out = _print_array(item, p); break;
  680. case _CJSON_OBJECT: out = _print_object(item, p); break;
  681. }
  682.  
  683. return out;
  684. }
  685.  
  686. // 同样 假定 item 和 p都是存在且不为NULL
  687. static char * _print_array(cjson_t item, tstr_t p)
  688. {
  689. char * ptr;
  690. cjson_t child = item->child;
  691. int ncut, i;
  692. // 得到孩子结点的深度
  693. for (ncut = ; (child); child = child->child)
  694. ++ncut;
  695. if (!ncut) { //没有孩子结点 直接空数组返回结果
  696. char* out = NULL;
  697. if (!(out = _ensure(p, )))
  698. strcpy(out, "[]");
  699. return out;
  700. }
  701.  
  702. i = p->len;
  703. if (!(ptr = _ensure(p, )))
  704. return NULL;
  705. *ptr = '[';
  706. ++p->len;
  707. for (child = item->child; (child); child = child->next) {
  708. _print_value(child, p);
  709. p->len = _update(p);
  710. if (child->next) {
  711. if (!(ptr = _ensure(p, )))
  712. return NULL;
  713. *ptr++ = ',';
  714. *ptr = '\0';
  715. p->len += ;
  716. }
  717. }
  718. if (!(ptr = _ensure(p, )))
  719. return NULL;
  720. *ptr++ = ']';
  721. *ptr = '\0';
  722. return p->str + i;
  723.  
  724. }
  725.  
  726. // 同样 假定 item 和 p都是存在且不为NULL, 相信这些代码是安全的
  727. static char * _print_object(cjson_t item, tstr_t p)
  728. {
  729. char * ptr;
  730. int i, ncut, len;
  731. cjson_t child = item->child;
  732.  
  733. // 得到孩子结点的深度
  734. for (ncut = ; child; child = child->child)
  735. ++ncut;
  736. if (!ncut) {
  737. char* out = NULL;
  738. if (!(out = _ensure(p, )))
  739. strcpy(out, "{}");
  740. return out;
  741. }
  742.  
  743. i = p->len;
  744. if (!(ptr = _ensure(p, )))
  745. return NULL;
  746. *ptr++ = '{';
  747. *ptr -= '\0';
  748. p->len += ;
  749. // 根据子结点 处理
  750. for (child = item->child; (child); child = child->next) {
  751. _print_string(child->key, p);
  752. p->len = _update(p);
  753.  
  754. //加入一个冒号
  755. if (!(ptr = _ensure(p, )))
  756. return NULL;
  757. *ptr++ = ':';
  758. p->len += ;
  759.  
  760. //继续打印一个值
  761. _print_value(child, p);
  762. p->len = _update(p);
  763.  
  764. //结算最后内容
  765. len = child->next ? : ;
  766. if ((ptr = _ensure(p, len + )) == NULL)
  767. return NULL;
  768. if (child->next)
  769. *ptr++ = ',';
  770. *ptr = '\0';
  771. p->len += len;
  772. }
  773. if (!(ptr = _ensure(p, )))
  774. return NULL;
  775. *ptr++ = '}';
  776. *ptr = '\0';
  777. return p->str + i;
  778. }
  779.  
  780. #define _INT_CJONSTR (256)
  781. /*
  782. * 这里是将 cjson_t item 转换成字符串内容,需要自己free
  783. * item : cjson的具体结点
  784. * : 返回生成的item的json串内容
  785. */
  786. char *
  787. cjson_print(cjson_t item) {
  788. struct tstr p;
  789. char * out;
  790. if (NULL == item) {
  791. CERR("item is error = %p!", item);
  792. return NULL;
  793. }
  794. // 构建内存
  795. p.str = sm_malloc(sizeof(char) * _INT_CJONSTR);
  796. p.size = _INT_CJONSTR;
  797. p.len = ;
  798.  
  799. out = _print_value(item, &p); //从值处理开始, 返回最终结果
  800. if (out == NULL) {
  801. sm_free(p.str);
  802. CERR("_print_value item:%p, p:%p is error!", item, &p);
  803. return NULL;
  804. }
  805. return sm_realloc(out, strlen(out) + ); // 体积变小 realloc返回一定成功
  806. }
  807.  
  808. // --------------------------------- 下面是 cjson 输出部分的辅助代码 -----------------------------------------
  809.  
  810. /*
  811. * 创建一个bool的对象 b==0表示false,否则都是true, 需要自己释放 cjson_delete
  812. * b : bool 值 最好是 _Bool
  813. * : 返回 创建好的json 内容
  814. */
  815. inline cjson_t
  816. cjson_newnull() {
  817. cjson_t item = _cjson_new();
  818. item->type = _CJSON_NULL;
  819. return item;
  820. }
  821.  
  822. inline cjson_t
  823. cjson_newbool(int b) {
  824. cjson_t item = _cjson_new();
  825. item->vd = item->type = b ? _CJSON_TRUE : _CJSON_FALSE;
  826. return item;
  827. }
  828.  
  829. inline cjson_t
  830. cjson_newnumber(double vd)
  831. {
  832. cjson_t item = _cjson_new();
  833. item->type = _CJSON_NUMBER;
  834. item->vd = vd;
  835. return item;
  836. }
  837.  
  838. inline cjson_t
  839. cjson_newstring(const char* vs)
  840. {
  841. cjson_t item = _cjson_new();
  842. item->type = _CJSON_STRING;
  843. item->vs = tstr_dup(vs);
  844. return item;
  845. }
  846.  
  847. inline cjson_t
  848. cjson_newarray(void)
  849. {
  850. cjson_t item = _cjson_new();
  851. item->type = _CJSON_ARRAY;
  852. return item;
  853. }
  854.  
  855. inline cjson_t
  856. cjson_newobject(void)
  857. {
  858. cjson_t item = _cjson_new();
  859. item->type = _CJSON_OBJECT;
  860. return item;
  861. }
  862.  
  863. /*
  864. * 按照类型,创建 对映类型的数组 cjson对象
  865. *目前支持 _CJSON_NULL _CJSON_BOOL/FALSE or TRUE , _CJSON_NUMBER, _CJSON_STRING
  866. * NULL => array 传入NULL, FALSE 使用char[],也可以传入NULL, NUMBER 只接受double, string 只接受char**
  867. * type : 类型目前支持 上面几种类型
  868. * array : 数组原始数据
  869. * len : 数组中元素长度
  870. * : 返回创建的数组对象
  871. */
  872. cjson_t
  873. cjson_newtypearray(int type, const void * array, int len) {
  874. int i;
  875. cjson_t n = NULL, p = NULL, a;
  876. // _DEBUG 模式下简单检测一下
  877. DEBUG_CODE({
  878. if(type < _CJSON_FALSE || type > _CJSON_STRING || len <=){
  879. CERR("check param is error! type = %d, len = %d.", type, len);
  880. return NULL;
  881. }
  882. });
  883.  
  884. // 这里是实际执行代码
  885. a = cjson_newarray();
  886. for(i=; i<len; ++i){
  887. switch(type){
  888. case _CJSON_NULL: n = cjson_newnull(); break;
  889. case _CJSON_FALSE: n = cjson_newbool(array? ((char*)array)[i] : ); break;
  890. case _CJSON_TRUE: n = cjson_newbool(array? ((char*)array)[i] : ); break;
  891. case _CJSON_NUMBER: n = cjson_newnumber(((double*)array)[i]); break;
  892. case _CJSON_STRING: n = cjson_newstring(((char**)array)[i]);break;
  893. }
  894. if(i){ //有你更好
  895. p->next = n;
  896. n->prev = p;
  897. }
  898. else
  899. a->child = n;
  900. p = n;
  901. }
  902. return a;
  903. }
  904.  
  905. /*
  906. * 在array中分离第idx个索引项内容.
  907. * array : 待处理的json_t 数组内容
  908. * idx : 索引内容
  909. * : 返回分离的json_t内容
  910. */
  911. cjson_t
  912. cjson_detacharray(cjson_t array, int idx) {
  913. cjson_t c;
  914. DEBUG_CODE({
  915. if(!array || idx<){
  916. CERR("check param is array:%p, idx:%d.", array, idx);
  917. return NULL;
  918. }
  919. });
  920.  
  921. for(c=array->child; idx> && c; c = c->next)
  922. --idx;
  923. if(c>){
  924. CERR("check param is too dig idx:sub %d.", idx);
  925. return NULL;
  926. }
  927. //这里开始拼接了
  928. if(c->prev)
  929. c->prev->next = c->next;
  930. if(c->next)
  931. c->next->prev = c->prev;
  932. if(c == array->child)
  933. array->child = c->next;
  934. c->prev = c->next = NULL;
  935. return c;
  936. }
  937.  
  938. /*
  939. * 在object json 中分离 key 的项出去
  940. * object : 待分离的对象主体内容
  941. * key : 关联的键
  942. * : 返回分离的 object中 key的项json_t
  943. */
  944. cjson_t
  945. cjson_detachobject(cjson_t object, const char * key) {
  946. cjson_t c;
  947. DEBUG_CODE({
  948. if(!object || !object->child || !key || !*key){
  949. CERR("check param is object:%p, key:%s.", object, key);
  950. return NULL;
  951. }
  952. });
  953.  
  954. for(c=object->child; c && tstr_icmp(c->key, key); c=c->next)
  955. ;
  956. if(!c) {
  957. CERR("check param key:%s => vlaue is empty.", key);
  958. return NULL;
  959. }
  960. if(c->prev)
  961. c->prev->next = c->next;
  962. if(c->next)
  963. c->next->prev = c->prev;
  964. if(c == object->child)
  965. object->child = c->next;
  966. c->prev = c->next = NULL;
  967. return c;
  968. }

主要测试文件 test_cjson.c

  1. #include <schead.h>
  2. #include <scjson.h>
  3.  
  4. // 测试 cjson 函数
  5. void test_readstr(void) {
  6. // 第二个 测试 json 串的解析
  7. puts("测试 cjson 是否可用");
  8. char text1[] = "{\n\"name\": \"Jack (\\\"Bee\\\") Nimble\", \n\"format\": {\"type\": \"rect\", \n\"width\": 1920, \n\"height\": 1080, \n\"interlace\": false,\"frame rate\": 24\n}\n}";
  9. TSTR_NEW(jstr1);
  10. jstr1->str = text1;
  11. cjson_t js = cjson_newtstr(jstr1);
  12.  
  13. cjson_t name = cjson_getobject(js, "name");
  14. printf("name => %s\n", name->vs);
  15.  
  16. cjson_t format = cjson_getobject(js, "format");
  17. printf("len(format) => %d\n", cjson_getlen(format));
  18.  
  19. cjson_t interlace = cjson_getobject(format, "interlace");
  20. printf("interlace => %d\n", cjson_getint(interlace));
  21.  
  22. cjson_delete(js);
  23.  
  24. //进行第三组测试
  25.  
  26. puts(" 测试 数组的读取");
  27. char text2[] = "[\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"]";
  28. TSTR_NEW(jstr2);
  29. jstr2->str = text2;
  30. js = cjson_newtstr(jstr2);
  31. int len = cjson_getlen(js);
  32. int i;
  33. for (i = ; i < len; ++i) {
  34. cjson_t item = cjson_getarray(js,i);
  35. printf("%d => %s.\n", i, item->vs);
  36. }
  37. cjson_delete(js);
  38.  
  39. puts("第三组测试");
  40. char text3[] = "[\n [0, -1, 0],\n [1, 0, 0],\n [0, 0, 1]\n ]\n";
  41. TSTR_NEW(jstr3);
  42. jstr3->str = text3;
  43. js = cjson_newtstr(jstr3);
  44. len = cjson_getlen(js);
  45. for (i = ; i < len; ++i) {
  46. cjson_t item = cjson_getarray(js, i);
  47. printf("%d => %d.\n", i, cjson_getlen(item));
  48. }
  49.  
  50. cjson_delete(js);
  51. exit(EXIT_SUCCESS);
  52. }
  53.  
  54. /*
  55. * simple c 框架业务层启动的代码
  56. */
  57. void test_readfile(void) {
  58.  
  59. // 测试json解析结果是否正常
  60. cjson_t goods = cjson_newfile("./goods.json");
  61.  
  62. // 数据输出
  63. int len = cjson_getlen(goods);
  64. printf("len = %d\n", len);
  65.  
  66. // 打印其中一个数据
  67. int idx = len / ;
  68. cjson_t jsidx = cjson_getarray(goods, idx);
  69. int ilen = cjson_getlen(jsidx);
  70. printf("ilen = %d\n", ilen);
  71. printf("[\"%s\", \"%s\", %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d]\n",
  72. cjson_getarray(jsidx, )->vs,
  73. cjson_getarray(jsidx, )->vs,
  74. cjson_getint(cjson_getarray(jsidx, )),
  75. cjson_getint(cjson_getarray(jsidx, )),
  76. cjson_getint(cjson_getarray(jsidx, )),
  77. cjson_getint(cjson_getarray(jsidx, )),
  78. cjson_getint(cjson_getarray(jsidx, )),
  79. cjson_getint(cjson_getarray(jsidx, )),
  80. cjson_getint(cjson_getarray(jsidx, )),
  81. cjson_getint(cjson_getarray(jsidx, )),
  82. cjson_getint(cjson_getarray(jsidx, )),
  83. cjson_getint(cjson_getarray(jsidx, )),
  84. cjson_getint(cjson_getarray(jsidx, )),
  85. cjson_getint(cjson_getarray(jsidx, )),
  86. cjson_getint(cjson_getarray(jsidx, )),
  87. cjson_getint(cjson_getarray(jsidx, ))
  88. );
  89.  
  90. cjson_delete(goods);
  91. exit(EXIT_SUCCESS);
  92. }

最后链接过程, 编译文件 Makefile

  1. CC = gcc
  2. DEBUG = -ggdb3 -Wall -D_DEBUG
  3. DIR = -I.
  4. LIB = -lm
  5. RUN = $(CC) $(DEBUG) -o $@ $^ $(LIB)
  6. RUNO = $(CC) -c -o $@ $< $(DIR)
  7. TEST = -nostartfiles -e $(*F)
  8. RUNMAIN = $(RUN) $(TEST)
  9.  
  10. all:test_readstr.out test_readfile.out
  11.  
  12. # 库文件编译
  13. %.o:%.c
  14. $(RUNO)
  15.  
  16. test_readstr.out:test_cjson.o scalloc.o tstr.o schead.o scjson.o
  17. $(RUNMAIN)
  18.  
  19. test_readfile.out:test_cjson.o scalloc.o tstr.o schead.o scjson.o
  20. $(RUNMAIN)
  21.  
  22. # 清除命令
  23. .PHONY:clean
  24. clean:
  25. rm -rf *.i *.s *.o *.out *~ ; ls

编译结果展示

分别测试解析串和文件结果如下

还有解析goods.json 文件结果的

到这里基本测试完毕了, 这个scjson 引擎也可以收官''截稿'', 欢迎尝试, 也就1200行代码. 很实在, 应该好懂吧. 扯一点对于开发中编码问题, 推荐用UTF-8编码,

对于配置资源, 和操作系统原始编码保持一致.

文末分享一个BUG, 很有意思.  是标准函数是fgetc引起的

  1. _Success_(return != EOF)
  2. _Check_return_opt_
  3. _ACRTIMP int __cdecl fgetc(
  4. _Inout_ FILE* _Stream
  5. );

当你用 int c = fgetc(txt) 的时候, c 总是 >=0 . 走 unsigend char 差不多. 因而造成了逻辑分支出错,  这里需要统一定义为 char c = fgetc(txt);

这个BUG在解析utf-8编码文件会遇到. 是不是很神奇. 切记不要趟坑.

后记 - OK

  错误是难免, 欢迎指正, 代码不好读, 说明你没有读过更加意外, 扯蛋的代码.

一个师傅三个徒弟 http://music.163.com/#/song?id=199768

c json实战引擎四 , 最后❤跳跃的更多相关文章

  1. c json实战引擎五 , 优化重构

    引言 scjson是一个小巧的纯c跨平台小巧引擎. 适用于替换老的cJSON引擎的场景. 数据结构和代码布局做了大量改进.优势体现在以下几个方面: 1) 跨平台 (window 10 + VS2017 ...

  2. C json实战引擎 三 , 最后实现部分辅助函数

    引言 大学读的是一个很时髦的专业, 学了四年的游戏竞技. 可惜没学好. 但认真过, 比做什么都认真. 见证了  ...... 打的所有游戏人物中 分享一位最喜爱 的 “I've been alone ...

  3. C json实战引擎 二 , 实现构造部分

    引言 这篇博文和前一篇 C json实战引擎一,实现解析部分设计是相同的,都是采用递归下降分析. 这里扯一点 假如你是学生 推荐一本书 给 大家 自制编程语言 http://baike.baidu.c ...

  4. C json实战引擎 一 , 实现解析部分

    引言 以前可能是去年的去年,写了一个 c json 解析引擎用于一个统计实验数据项目开发中. 基本上能用. 去年在网上 看见了好多开源的c json引擎 .对其中一个比较标准的 cJSON 引擎 深入 ...

  5. c json实战引擎六 , 感觉还行

    前言 看到六, 自然有 一二三四五 ... 为什么还要写呢.  可能是它还需要活着 : ) 挣扎升级中 . c json 上面代码也存在于下面项目中(维护的最及时) structc json 这次版本 ...

  6. MySQL数据库性能优化与监控实战(阶段四)

    MySQL数据库性能优化与监控实战(阶段四) 作者 刘畅 时间 2020-10-20 目录 1 sys数据库 1 2 系统变量 1 3 性能优化 1 3.1 硬件层 1 3.2 系统层 1 3.3 软 ...

  7. Javascript多线程引擎(四)

    Javascript多线程引擎(四)--之C语言单继承 因为使用C语言做为开发语言, 而C语言在类的支持方面几乎为零, 而Javascript语言的Object类型是一个非常明显的类支持对象,所以这里 ...

  8. python机器学习实战(四)

    python机器学习实战(三) 版权声明:本文为博主原创文章,转载请指明转载地址 www.cnblogs.com/fydeblog/p/7364317.html 前言 这篇notebook是关于机器学 ...

  9. apollo客户端springboot实战(四)

    1. apollo客户端springboot实战(四) 1.1. 前言   经过前几张入门学习,基本已经完成了apollo环境的搭建和简单客户端例子,但我们现在流行的通常是springboot的客户端 ...

随机推荐

  1. Go语言【第一篇】:Go初识

    Go语言特色 简洁.快速.安全 并行.有趣.开源 内存管理.数据安全.编译迅速 Go语言用途 Go语言被设计成一门应用于搭载Web服务器,存储集群或类似用途的巨型中央服务器的系统编程语言.对于高性能分 ...

  2. 廖雪峰老师Python教程读后笔记

    廖老师网站:http://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000 花几天时间看了廖老师的 ...

  3. Gevent-socket

    1. 通过Gevent实现单线程下的多socket并发. server 端: #server side import sys import socket import time import geve ...

  4. [BZOJ4036] [HAOI2015]按位或

    传送门:https://lydsy.com/JudgeOnline/problem.php?id=4036 Description 刚开始你有一个数字0,每一秒钟你会随机选择一个[0,2^n-1]的数 ...

  5. Linux实验一

    一.Linux 简介 1.Linux 就是一个操作系统,就像你多少已经了解的 Windows(xp,7,8)和 Max OS , 我们的 Linux 也就是系统调用和内核那两层,当然直观的来看,我们使 ...

  6. HDOJ(HDU).2159 FATE (DP 带个数限制的完全背包)

    HDOJ(HDU).2159 FATE (DP 带个数限制的完全背包) 题意分析 与普通的完全背包大同小异,区别就在于多了一个个数限制,那么在普通的完全背包的基础上,增加一维,表示个数.同时for循环 ...

  7. Linux环境下用Weblogic发布项目【一】 -- 安装Weblogic

     一.Weblogic安装系统环境: 1.前提条件: a.在笔记本[Windows7]上安装远程连接Linux软件:F-Secure SSH File Transfer Trial[简写为:FSSH] ...

  8. HDU 5656

    CA Loves GCD Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)To ...

  9. Codeforces Round #342 (Div. 2) C

    C. K-special Tables time limit per test 2 seconds memory limit per test 256 megabytes input standard ...

  10. 重载(Overload)和重写(Override)的区别。重载的方法能否根据返回类型进行区分?

    方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性.重载发生在一个类中,同名的方法如果有不同的参数列表(参数类型不同.参数个数不同或者二者都不同)则视 ...