cjson库的使用以及源码阅读

摘要:
Cjson是一个用c语言开发的json解析库。免费开源只有一个c文件和一个h文件。将头文件与源代码分开,然后向代码中添加注释。至于测试例程,cjson的源代码提供了一个测试。c、 完成工作后释放char*externachar*cJSON _打印;//将json数据转换为文本数据,无需任何格式,便于转发和存储。
cjson是一个用c语言开发的json解析库,免费开源只有一个c文件和一个h文件。
json和xml功能相似,可以用来传输数据,存储数据以及表达程序当前的状态。
1、下载cjson的源码
        https://github.com/DaveGamble/cJSON
2、阅读readme文件可以大概的了解一下cjson的介绍以及使用方法,我尝试着把readme文件做了一下翻译,水平有限,大概意思写在了“cjson工程的readme文件翻译”,可以参考原文来对照看,如果只是想快速应用一下cjson库的话,看完应该能知道如何使用这个cjson库了。
3、源码分析
    如果本着不了解其实现无法安心使用的心态的话,可以看一下下面的源码解析。
    将头文件和源码进行分开,然后注释都添加在了代码里(基本上都是根据自己对英文的理解进行翻译的),至于测试例程,其实cjson的源码提供了一个test.c。这个文件里面里面提供了一个比较全面的测试用例。附件里面是整理完格式和加了注释后的cJSON源码

cJSON.h:
  1. #ifndef cJSON__h
  2. #define cJSON__h
  3. #ifdef __cplusplus
  4. extern "C"
  5. {
  6. #endif
  7. //以宏的方式定义出的几种cJson对象的类型
  8. #define cJSON_False 0
  9. #define cJSON_True 1
  10. #define cJSON_NULL 2
  11. #define cJSON_Number 3
  12. #define cJSON_String 4
  13. #define cJSON_Array 5
  14. #define cJSON_Object 6
  15. #define cJSON_IsReference 256
  16. #define cJSON_StringIsConst 512
  17. //cJSON的数据结构
  18. typedef struct cJSON
  19. {
  20. /* next/prev 用来遍历所有的数组或者对象链表. 一般来说可以调用GetArraySize/GetArrayItem/GetObjectItem 进行操作*/
  21. struct cJSON *next,*prev;
  22. /* 一个数组或者对象会有一个孩子节点指针指向一个对象或者数组链 */
  23. struct cJSON *child;
  24. /* 这个节点的类型, 为上面定义的宏 */
  25. int type;
  26. /* 是节点的值 如果节点的类型是cJSON_String的话 */
  27. char *valuestring;
  28. /* 是节点的值 如果节点的类型是cJSON_Number的话 */
  29. int valueint;
  30. /* 是节点的值 如果节点的类型是cJSON_Number的话 */
  31. double valuedouble;
  32. /* 节点的名字*/
  33. char *string;
  34. } cJSON;
  35. //钩子?将申请内存和释放内存的接口进行管理。
  36. typedef struct cJSON_Hooks {
  37. void *(*malloc_fn)(size_t sz);
  38. void (*free_fn)(void *ptr);
  39. } cJSON_Hooks;
  40. //为cJSON提供 malloc realloc 和 free函数
  41. extern void cJSON_InitHooks(cJSON_Hooks* hooks);
  42. //提供一个JSON的内存块,返回出从value传入的字符串中携带的json信息使你后续可以进行提取。在完成工作之后要使用cJSON_Delete进行释放
  43. extern cJSON *cJSON_Parse(const char *value);
  44. //将一个json数据转换为文本数据,用来方便转发和存储。在完成工作之后要释放char *
  45. extern char *cJSON_Print(cJSON *item);
  46. //将一个json数据转换为不含有任何格式的文本数据,用来方便转发和存储。在完成工作之后要释放char *
  47. extern char *cJSON_PrintUnformatted(cJSON *item);
  48. /* 使用缓存的策略将json数据打印到缓冲区中. prebuffer是预测的缓存大小. 认为可以很好的减少内存重复分配.
  49. * fmt=0 不含有任何格式,
  50. * fmt=1 含有格式
  51. */
  52. extern char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt);
  53. /* 删除整个json结构体和其所有子项 */
  54. extern void cJSON_Delete(cJSON *c);
  55. /* 返回一个对象或者数组中所有的元素个数*/
  56. extern int cJSON_GetArraySize(cJSON *array);
  57. /* 检索数组array中第item个元素,不成功则返回NULL */
  58. extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);
  59. /*获取"string"指定的对象. 不区分大小写. */
  60. extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
  61. /* 用来分析错误的解析.返回一个指向解析错误位置的指针。你可能需要从这个位置往回检查几个字符. 解析成功则返回0. */
  62. extern const char *cJSON_GetErrorPtr(void);
  63. /*下面的这些调用用来根据指定的类型创建cjson的节点。*/
  64. extern cJSON *cJSON_CreateNull(void);
  65. extern cJSON *cJSON_CreateTrue(void);
  66. extern cJSON *cJSON_CreateFalse(void);
  67. extern cJSON *cJSON_CreateBool(int b);
  68. extern cJSON *cJSON_CreateNumber(double num);
  69. extern cJSON *cJSON_CreateString(const char *string);
  70. extern cJSON *cJSON_CreateArray(void);
  71. extern cJSON *cJSON_CreateObject(void);
  72. /*下面的这些用来建立count个节点*/
  73. extern cJSON *cJSON_CreateIntArray(const int *numbers,int count);
  74. extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count);
  75. extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count);
  76. extern cJSON *cJSON_CreateStringArray(const char **strings,int count);
  77. /*将指定的节点添加到数组或者对象中 */
  78. extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
  79. extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);
  80. /* 当字符串是常量的时候使用下面这个接口 */
  81. extern void cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item);
  82. /*添加指定的节点到指定的对象或者数组中. 把一个存在的cJSON添加到一个新的cJSON但是又不想销毁已经存在的这个cJSON使用这一组接口*/
  83. extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
  84. extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item);
  85. /* 从一个数组或者对象中删除指定的节点 */
  86. extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which);
  87. extern void cJSON_DeleteItemFromArray(cJSON *array,int which);
  88. extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string);
  89. extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string);
  90. /* 更新数组中的节点. */
  91. extern void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem); /* 将原有的节点右移. */
  92. extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem);
  93. extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
  94. /* 复制一个cJSON对象 */
  95. extern cJSON *cJSON_Duplicate(cJSON *item,int recurse);
  96. /* Duplicate会创建一个与传入参数完全相同的对象, 在新的内存中需要释放。
  97. *当recurse!=0,将会复制这个对象中的所有的孩子节点。
  98. *返回对象中的item->next 和 ->prev指针通常是0。
  99. */
  100. /* ParseWithOpts允许你去指定或者检查字符串是否以NULL结尾, 同时可以检索解析后的字符串的最后一个位置. */
  101. extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated);
  102. //一个精简后的解析框架
  103. extern void cJSON_Minify(char *json);
  104. /* 宏,用来做快速建立并添加操作. */
  105. #define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull())
  106. #define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue())
  107. #define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse())
  108. #define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b))
  109. #define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n))
  110. #define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
  111. /* 当赋值一个整数的时候, 需要对浮点数也进行同时赋值. */
  112. #define cJSON_SetIntValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val))
  113. #define cJSON_SetNumberValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val))
  114. #ifdef __cplusplus
  115. }
  116. #endif
  117. #endif

    以上为cjson的头文件中的内容,其中定义cjson结构体以及操作json数据的接口,对于cjson结构体来说通过之前的readme文件可以大致的根据一个样例数据进行示例其在内存中的组织方式,而各个接口的实现则在后续的cjson.c的分析中进行展开。
    在绘图时,对于cjson结构的组织如下图所示:
cjson库的使用以及源码阅读第1张
图一:cjson的数据结构示意图
  1. {
  2. "name": "Jack ("Bee") Nimble",
  3. "format": {
  4. "type": "rect",
  5. "width": 1920,
  6. "height": 1080,
  7. "interlace": false,
  8. "frame rate": 24
  9. }
  10. }
    cjson将上面所示例的json数据在内存中组织方式如下图所示(char*实际情况应为指向动态申请的内存的指针,但为了方便起见图中约定char*使用值直接替换):
cjson库的使用以及源码阅读第2张
图二:示例json的组织方式
    下面是对于cJSON.c中的源码分析。
cJSON.c
  1. /* cJSON */
  2. /* JSON parser in C. */
  3. #include <string.h>
  4. #include <stdio.h>
  5. #include <math.h>
  6. #include <stdlib.h>
  7. #include <float.h>
  8. #include <limits.h>
  9. #include <ctype.h>
  10. #include "cJSON.h"
  11. /* ep静态全局指针,指向字符串,*/
  12. static const char *ep;
  13. /*返回ep所指向字符串的地址,使用const修饰返回值,说明ep所指向的字符串是常量,
  14. 应该是写死在出错方式上。所以不需要释放其返回的指针的*/
  15. const char *cJSON_GetErrorPtr(void)
  16. {
  17. return ep;
  18. }
  19. /*忽略大小写比较字符串s1和s2, 参数使用const进行修饰,说明内部不会修改这两个值。
  20. *static说明文件内作用域,返回整数,
  21. *=0 - 相等;
  22. *>0 - s1>s2;
  23. *<0 - s1<s2;
  24. */
  25. static int cJSON_strcasecmp(const char *s1,const char *s2)
  26. {
  27. //s1==NULL的情况下,如果s2也是NULL就相等,不然就是s1<s2;
  28. if (!s1)
  29. return (s1==s2)?0:1;
  30. //s1不是NULL,但是s2是NULL,那么s1>s2
  31. if (!s2)
  32. return 1;
  33. //不区分大小写,即都以小写形式进行比较,循环比较每个字符。不相等则跳出循环。
  34. for(; tolower(*s1) == tolower(*s2); ++s1, ++s2)
  35. if(*s1 == 0)//如果这个条件为真,说明s1==s2,s1==NULL。即,两个字串不区分大小写相同
  36. return 0;
  37. //将不相同的那个字符的小写形式进行相减,可以得到两个串的大小。
  38. //强制转换防止报错吧。
  39. return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2);
  40. }
  41. /*对静态全局函数指针变量进行赋值,使cJSON_malloc=malloc,使cJSON_free=free*/
  42. static void *(*cJSON_malloc)(size_t sz) = malloc;
  43. static void (*cJSON_free)(void *ptr) = free;
  44. /*静态作用域,返回char*类型指针,该指针指向从str中复制出内容的一块新申请的内存地址,str为const,不可修改*/
  45. static char* cJSON_strdup(const char* str)
  46. {
  47. size_t len;
  48. char* copy;
  49. //获取字符串长度,并考虑了最后一个结束符号。
  50. len = strlen(str) + 1;
  51. //申请len个长度内存并类型转换和测试是否申请成功,不成功就返回NULL。
  52. if (!(copy = (char*)cJSON_malloc(len))) return 0;
  53. //申请成功就拷贝len长度个串进去。然后将首地址返回
  54. memcpy(copy,str,len);
  55. return copy;
  56. }
  57. /*初始化钩子,其实所谓钩子在这里也就是内存申请和释放接口*/
  58. void cJSON_InitHooks(cJSON_Hooks* hooks)
  59. {
  60. //判断传入的指针为NULL,那么就使用系统的申请和释放接口
  61. if (!hooks) { /* Reset hooks */
  62. cJSON_malloc = malloc;
  63. cJSON_free = free;
  64. return;
  65. }
  66. //然后根据传入参数中是否携带指定的申请和释放接口,进行选择使用哪一个内存接口。
  67. cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc;
  68. cJSON_free = (hooks->free_fn)?hooks->free_fn:free;
  69. }
  70. /*申请一个cJSON结构体大小的内存,初始化为0,静态作用域*/
  71. static cJSON *cJSON_New_Item(void)
  72. {
  73. //申请内存,测试,赋值为0,返回指针。
  74. cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
  75. if (node)
  76. memset(node,0,sizeof(cJSON));
  77. return node;
  78. }
  79. /*删除一个cJSON结构体,还应该循环将它的所有子项进行删除清理*/
  80. /*对于c->type的0、1、2、3、4、5、6和256以及512做与预算到底是为什么要这么做呢?*/
  81. void cJSON_Delete(cJSON *c)
  82. {
  83. cJSON *next;
  84. while (c)
  85. {
  86. //记录c节点的下一个节点。
  87. next=c->next;
  88. //c->type&cJSON_IsReference将type与256做与运算,判断有孩子节点则进行递归
  89. if (!(c->type&cJSON_IsReference) && c->child)
  90. cJSON_Delete(c->child);
  91. //valuestring不为NULL,需要释放内存
  92. if (!(c->type&cJSON_IsReference) && c->valuestring)
  93. cJSON_free(c->valuestring);
  94. //string不为NULL,需要释放内存
  95. if (!(c->type&cJSON_StringIsConst) && c->string)
  96. cJSON_free(c->string);
  97. //删除这个节点
  98. cJSON_free(c);
  99. //继续下一个节点
  100. c=next;
  101. }
  102. }
  103. /*将输入的num解析成一个数字,然后将结果填充到节点中
  104. *静态作用域,const返回值为传入字串解析完后第一个不为数值的位置,解析num到item中
  105. */
  106. static const char *parse_number(cJSON *item,const char *num)
  107. {
  108. double n=0,sign=1,scale=0;
  109. int subscale=0,signsubscale=1;
  110. //是否为负数
  111. if (*num=='-')
  112. sign=-1,num++;
  113. //是否为0
  114. if (*num=='0')
  115. num++;
  116. //如果是十进制就进行解析,临时变量存到n中
  117. if (*num>='1' && *num<='9')
  118. do
  119. n=(n*10.0)+(*num++ -'0');
  120. while (*num>='0' && *num<='9');
  121. //如果存在小数点,将数字继续转存到n的末尾以整数方式,但是使用scale记录有几位小数
  122. if (*num=='.' && num[1]>='0' && num[1]<='9')
  123. {
  124. num++;
  125. do
  126. n=(n*10.0)+(*num++ -'0'),scale--;
  127. while (*num>='0' && *num<='9');
  128. }
  129. //如果是指数计数方式,记录指数符号,然后将数字进行转存到subscale中
  130. if (*num=='e' || *num=='E')
  131. {
  132. num++;
  133. if (*num=='+')
  134. num++;
  135. else if (*num=='-')
  136. signsubscale=-1,num++;
  137. while (*num>='0' && *num<='9')
  138. subscale=(subscale*10)+(*num++ - '0');
  139. }
  140. /* number = +/- number.fraction * 10^+/- exponent */
  141. n=sign*n*pow(10.0,(scale+subscale*signsubscale));
  142. //将计算到的值赋值到item中,int和double项都要赋值。类型赋值为NUM
  143. item->valuedouble=n;
  144. item->valueint=(int)n;
  145. item->type=cJSON_Number;
  146. return num;
  147. }
  148. /*返回大于x的最小的2的幂,静态作用域*/
  149. /*在实现上就是把x占用到的最高位为1的位到第0位,都置位为1*/
  150. static int pow2gt (int x)
  151. {
  152. //--x是为了防止x直接就是2的幂的情况。
  153. --x;
  154. x|=x>>1; x|=x>>2; x|=x>>4; x|=x>>8; x|=x>>16;
  155. return x+1;
  156. }
  157. //定义一个printbuffer类型,主要是用来将json数据打印到缓冲区时,进行提供缓存空间的信息。
  158. typedef struct
  159. {
  160. char *buffer; //缓存地址指针
  161. int length; //缓存当前的长度
  162. int offset; //缓存当前已经使用到的位置。
  163. } printbuffer;
  164. /*
  165. *ensure 意为确保的意思,这里可以理解为,确保p所指向的缓冲区中能够提供needed大小的缓冲给打印功能使用
  166. *静态作用域,返回缓冲区中可以继续使用的空间的位置。needed在里面做了局部变量,修改使用均无碍,
  167. *减少了一个中间变量。
  168. */
  169. static char* ensure(printbuffer *p,int needed)
  170. {
  171. char *newbuffer;int newsize;
  172. //如果p=NULL,或者p->buffer=NULL,那么为运行时检查错误,返回空指针
  173. if (!p || !p->buffer)
  174. return 0;
  175. //计算将当前所需的空间加上之前已用的空间一共需要内存的数目
  176. needed+=p->offset;
  177. //如果现在p->buffer足够容下所有的值,那么就返回当前缓存中最后一个可用的位置
  178. if (needed<=p->length)
  179. return p->buffer+p->offset;
  180. /*下面为处理当前缓存不能存下所有的信息的时候*/
  181. //计算大于当前所需数目的最小2de幂,用来分配内存数目
  182. newsize=pow2gt(needed);
  183. //申请内存并做错误检查,如果失败那么就置空后返回空指针。
  184. newbuffer=(char*)cJSON_malloc(newsize);
  185. if (!newbuffer)
  186. {
  187. cJSON_free(p->buffer);
  188. p->length=0,
  189. p->buffer=0;
  190. return 0;
  191. }
  192. //申请成功后将原有数据拷贝到新空间中。
  193. if (newbuffer)
  194. memcpy(newbuffer,p->buffer,p->length);
  195. //释放原指针,并更新新的缓存信息,然后返回缓存中第一个可用的内存位置。
  196. cJSON_free(p->buffer);
  197. p->length=newsize;
  198. p->buffer=newbuffer;
  199. return newbuffer+p->offset;
  200. }
  201. /*静态作用域函数,意为更新,传入参数为缓存结构,返回当前缓存区已使用的内存偏移量*/
  202. static int update(printbuffer *p)
  203. {
  204. char *str;
  205. //运行时错误检查
  206. if (!p || !p->buffer)
  207. return 0;
  208. //将str定义到新加入缓存的数据的首地址。然后使用strlen计算新添加长度后加上原有的偏移量进行返回。
  209. str=p->buffer+p->offset;
  210. return p->offset+strlen(str);
  211. }
  212. /*静态作用域,将item中的数字打印成字符串,
  213. *当p不为NULL时,使用的是p所指向的内存缓冲区,当p为NULL时,使用的是单独申请的内存
  214. */
  215. static char *print_number(cJSON *item,printbuffer *p)
  216. {
  217. char *str=0;
  218. double d=item->valuedouble;
  219. //如果item的数值为0,使用两个字节,根据p是否为空,决定使用从哪里分配的缓存,并将字符串"0"拷贝到缓存中。
  220. if (d==0)
  221. {
  222. if (p) str=ensure(p,2);
  223. else str=(char*)cJSON_malloc(2); /* special case for 0. */
  224. if (str) strcpy(str,"0");
  225. }
  226. //如果item的数值为整数,21个char肯定装的下,并验证数值的正确性。
  227. //(fabs(((double)item->valueint)-d)<=DBL_EPSILON,标示差小于最小误差值,即可以理解为整数,并用INT_MAX、INT_MIN,验证合法性数据。
  228. else if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN)
  229. {
  230. if (p) str=ensure(p,21);
  231. else str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */
  232. if (str) sprintf(str,"%d",item->valueint);
  233. }
  234. //走到这里,肯定是小数,选用64个字节较为合适。
  235. else
  236. {
  237. if (p) str=ensure(p,64);
  238. else str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */
  239. if (str)
  240. {
  241. //如果小数值特别接近零,并且整数部分值特别大,那么就以xxxxx.0方式输出
  242. if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60)sprintf(str,"%.0f",d);
  243. //如果数值比1.0e-6小或者比1.0e9数值大,那么比较适合用科学计数法标示
  244. else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d);
  245. //剩余部分直接用小数点形式进行输出
  246. else sprintf(str,"%f",d);
  247. }
  248. }
  249. return str;
  250. }
  251. /*从16进制整数的字符串表达方式转换成无符号整数*/
  252. /*静态作用域,返回无符号整数,参数不可修改*/
  253. static unsigned parse_hex4(const char *str)
  254. {
  255. //将字符串的字符逐个取出进行分析,然后计算到整数中。然后将h左移一个直接再进行下一个数字的解析。最后完成4个字节的整数解析
  256. unsigned h=0;
  257. if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
  258. h=h<<4;str++;
  259. if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
  260. h=h<<4;str++;
  261. if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
  262. h=h<<4;str++;
  263. if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0;
  264. return h;
  265. }
  266. /*将输入的文本解析为非转意的c的字符串,然后填充到item中,应该保证str是已经去除开头空字符的串,
  267. *,此处的静态字符数组,是用来做utf格式转换的,返回值为解析出一个字符串之后的首地址
  268. */
  269. static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
  270. static const char *parse_string(cJSON *item,const char *str)
  271. {
  272. const char *ptr=str+1;
  273. char *ptr2;
  274. char *out;
  275. int len=0;
  276. unsigned uc,uc2;
  277. //如果str不以"引号,开头,那么不是一个字符串。
  278. if (*str!='"') {ep=str;return 0;} /* not a string! */
  279. while (*ptr!='"' && *ptr && ++len)
  280. if (*ptr++ == '\')
  281. ptr++; /* Skip escaped quotes. */
  282. //分配内存并进行检查
  283. out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */
  284. if (!out) return 0;
  285. ptr=str+1;ptr2=out;
  286. while (*ptr!='"' && *ptr)
  287. {
  288. if (*ptr!='\')
  289. *ptr2++=*ptr++;
  290. else
  291. {//如果以反斜杠开头的转义字符,则进行下诉的语义转换。只有utf格式转换不是很了解。
  292. ptr++;
  293. switch (*ptr)
  294. {
  295. case 'b': *ptr2++=''; break;
  296. case 'f': *ptr2++='f'; break;
  297. case 'n': *ptr2++=' '; break;
  298. case 'r': *ptr2++=' '; break;
  299. case 't': *ptr2++=' '; break;
  300. case 'u': /* transcode utf16 to utf8. */
  301. uc=parse_hex4(ptr+1);ptr+=4; /* get the unicode char. */
  302. //utf16和utf8之间格式的转换
  303. if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; /* check for invalid. */
  304. if (uc>=0xD800 && uc<=0xDBFF) /* UTF16 surrogate pairs. */
  305. {
  306. if (ptr[1]!='\' || ptr[2]!='u') break; /* missing second-half of surrogate. */
  307. uc2=parse_hex4(ptr+3);ptr+=6;
  308. if (uc2<0xDC00 || uc2>0xDFFF) break; /* invalid second-half of surrogate. */
  309. uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF));
  310. }
  311. len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len;
  312. switch (len) {
  313. case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
  314. case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
  315. case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
  316. case 1: *--ptr2 =(uc | firstByteMark[len]);
  317. }
  318. ptr2+=len;
  319. break;
  320. default: *ptr2++=*ptr; break;
  321. }
  322. ptr++;
  323. }
  324. }
  325. //在结尾填充上'

免责声明:内容来源于网络,仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇安装VCenter 6.7的系统要求idea安装完成后点击没反应 打不开下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Asp.Net Mvc4 Webapi Request获取参数

最近用mvc4中的WEBAPI,发现接收参数不是很方便,跟传统的request.querystring和request.form有很大区别,在网上搜了一大圈,各种方案都有,但不是太详细,于是跟踪Action中的变量,仔细查看,最后发现了解决方案,下面是代码: public void Post([FromBody]string value)...

C++中的动态绑定

C++中基类和派生类遵循类型兼容原则:即可用派生类的对象去初始化基类的对象,可用派生类的对象去初始化基类的引用,可用派生类对象的地址去初始化基类对象指针。 C++中动态绑定条件发生需要满足2个条件: 1:只有指定为虚函数的成员函数才能进行动态绑定,成员函数默认为非虚函数,非虚函数不能进行动态绑定 2:必须通过基类类型的引用或指针进行函数调用 基类类型引用和...

测试开发进阶——spring boot——MVC——get访问——使用@RequestParam获取参数(参数个数一致)

控制器: package com.awaimai.web; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframewo...

C++学习笔记九顺序容器(二) ForFreeDom 博客园

C++学习笔记九-顺序容器(二) - ForFreeDom - 博客园 一、插入操作如何影响容器的选择: 1.list 容器表示不连续的内存区域,允许向前和向后逐个遍历元素。在任何位置都可高效地 insert 或 erase 一个元素。插入或删除 list 容器中的一个元素不需要移动任何其他元素。另一方面,list 容器不支持随机访问,访问某个元素要求...

mysql增删改和学生管理sql

importpymysql #2.建连 conn = pymysql.connect("localhost","root",'root','李森') print(conn) #3.获取游标 cur =conn.cursor() #4.增 sql="insert into student_1 values(default,%s,%s,%s,%s)"cur.e...

(八)Asp.NET中三层架构的应用

一、 什么是三层架构? 生活中的三层 初始结构: 在现实生活中,如果老王开了一家饭店,前期顾客比较少,自己来招待客人、在后厨炒菜、每天去市场采购食材。但是随着顾客量的增加,饭店的生意越来越兴隆,自己一个人单干忙的不可开交。就好比我们的软件系统一样,我们的用户是浏览我们的网页的,主要的功能是体现在UI层面,用户和系统产生交互,UI层面需要接收用户的数据信...