zoukankan      html  css  js  c++  java
  • c json实战引擎四 , 最后❤跳跃

    引言  - 以前那些系列

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

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

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

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

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

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

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

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

         本文最终的资源  test_scjson.zip

    前言  - 那就从json文件看起

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

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

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

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

    // 从json文件中解析出最简json数据
    static tstr_t _cjson_newfile(const char * path) {
        char c, n;
        tstr_t tstr;
        FILE * txt = fopen(path, "r");
        if (NULL == txt) {
            CERR("fopen r %s is error!", path);
            return NULL;
        }
    
        //这里创建文本串对象
        tstr = tstr_new(NULL);
    
        while ((c = fgetc(txt)) != EOF) {
            // step 1 : 处理字符串
            if (c == '"') {
                tstr_append(tstr, c);
                for (n = c; ((c = fgetc(txt)) != EOF) && (c != '"' || n == '\'); n = c)
                    tstr_append(tstr, c);
                if (EOF != c)
                    tstr_append(tstr, c);
                continue;
            }
    
            // step 2 : 处理不可见特殊字符
            if (c < '!')
                continue;
    
            if (c == '/') {
                // step 3 : 处理 // 解析到行末尾
                n = fgetc(txt);
                if (n == '/') {
                    while ((c = fgetc(txt)) != EOF && c != '
    ')
                        ;
                    continue;
                }
    
                // step 4 : 处理 /*
                if (n == '*') {
                    while ((c = fgetc(txt)) != EOF) {
                        if (c == '*') {
                            n = fgetc(txt);
                            if (n == '/')
                                break;
                            ungetc(n, txt);
                        }
                    }
                    continue;
                }
                ungetc(n, txt);
            }
    
            // step 5 : 合法数据直接保存
            tstr_append(tstr, c);
        }
    
        fclose(txt);//很重要创建了就要释放,否则会出现隐藏的句柄bug
        return tstr;
    }

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

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

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

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

      4. /* 舍弃到 */ 为止

      5. 合法字符直接放入

    附注ascii码表如下

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

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

    /*
     * 将 jstr中 不需要解析的字符串都去掉,并且纪念mini 比男的还平
     * jstr        : 待处理的json串
     *            : 返回压缩后的json串长度
     */
    static int _cjson_mini(char * jstr) {
        char c, *in = jstr, *to = jstr;
    
        while ((c = *to)) {
            // step 1 : 处理字符串
            if (c == '"') {
                *in++ = c;
                while ((c = *++to) && (c != '"' || to[-1] == '\'))
                    *in++ = c;
                if (c) {
                    *in++ = c;
                    ++to;
                }
                continue;
            }
    
            // step 2 : 处理不可见特殊字符
            if (c < '!') {
                ++to;
                continue;
            }
    
            if (c == '/') {
                // step 3 : 处理 // 解析到行末尾
                if (to[1] == '/') {
                    while ((c = *++to) && c != '
    ')
                        ;
                    continue;
                }
    
                // step 4 : 处理 /*
                if (to[1] == '*') {
                    while ((c = *++to) && (c != '*' || to[1] != '/'))
                        ;
                    if (c)
                        to += 2;
                    continue;
                }
            }
    
            // step 5 : 合法数据直接保存
            *in++ = *to++;
        }
    
        *in = '';
        return in - jstr;
    }

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

    正文  - 测试环境搭建

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

     schead.h 

    #ifndef _H_SIMPLEC_SCHEAD
    #define _H_SIMPLEC_SCHEAD
    
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>
    #include <stdint.h>
    #include <stddef.h>
    #include <assert.h>
    #include <stdbool.h>
    #include <scalloc.h>
    
    /*
     * error        => 以后再说
     * 跨平台的丑陋从这里开始
     * __GNUC        => linux 平台特殊操作
     * __MSC_VER    => window 平台特殊操作
     */
    #ifdef __GNUC__  // 下面是依赖GCC编译器实现
    
    #include <unistd.h>
    #include <sys/time.h>
    #include <termio.h>
    
    // 统一的程序睡眠宏, 单位是毫秒颗粒度
    #define SLEEPMS(m) 
            usleep(m * 1000)
    
    // 屏幕清除宏, 依赖系统脚本
    #define CONSOLECLS() 
            system("printf 'ec'")
    
    /*
     * 得到用户输入的一个字符
     *        : 返回得到字符
     */
    extern int sh_getch(void);
    
    #elif _MSC_VER // 下面是依赖Visual Studio编译器实现
    
    #include <Windows.h>
    #include <direct.h> 
    #include <conio.h>
    
    // window 删除目录宏
    #define rmdir        _rmdir
    
    // window 上用_getch 替代了getch, 这里为了让其回来
    #define sh_getch    _getch
    
    #define CONSOLECLS() 
            system("cls")
    
    #define SLEEPMS(m) 
            Sleep(m)    
    
    #else
        #error "error : Currently only supports the Visual Studio and GCC!"
    #endif
    
    
     /*
      * 错误定义枚举 用于判断返回值状态的状态码 RT_*表示返回标志
      *    使用举例 :
      
        int flag = scconf_get("pursue");
        if(flag < RT_SuccessBase) {
            sclog_error("get config %s error! flag = %d.", "pursue", flag);
            exit(EXIT_FAILURE);
        }
      
      * 这里是内部 使用的通用返回值 标志. >=0 表示成功, <0 表示失败的情况
      */
    typedef enum {
        RT_SuccessBase    = 00,                //结果正确的返回宏
        RT_ErrorBase    = -1,                //错误基类型, 所有错误都可用它, 在不清楚的情况下
        RT_ErrorParam    = -2,                //调用的参数错误
        RT_ErrorMalloc    = -3,                //内存分配错误
        RT_ErrorFopen    = -4,                //文件打开失败    
        RT_ErrorClose    = -5,                //文件描述符读取关闭, 读取完毕也会返回这个
    } flag_e;
    
    /*
     * 定义一些通用的函数指针帮助,主要用于基库的封装中
     * 有构造函数, 释放函数, 比较函数等
     */
    typedef void *    (* pnew_f)();
    typedef void    (* vdel_f)(void * node);
    // icmp_f 最好 是 int cmp(const void * ln, const void * rn); 标准结构
    typedef int        (* icmp_f)();
    // 循环操作函数, arg 外部参数, node 内部节点
    typedef flag_e    (* each_f)(void * node, void * arg);
    
    /*
     * c 如果是空白字符返回 true, 否则返回false
     * c : 必须是 int 值,最好是 char 范围
     */
    #define sh_isspace(c) 
        ((c==' ')||(c>='	'&&c<='
    '))
    
    // 3.0 浮点数据判断宏帮助, __开头表示不希望你使用的宏
    #define __DIFF(x, y)                ((x)-(y))                    //两个表达式做差宏
    #define __IF_X(x, z)                ((x)<z && (x)>-z)            //判断宏,z必须是宏常量
    #define EQ(x, y, c)                    EQ_ZERO(__DIFF(x,y), c)        //判断x和y是否在误差范围内相等
    
    // 3.1 float判断定义的宏
    #define _FLOAT_ZERO                (0.000001f)                        //float 0的误差判断值
    #define EQ_FLOAT_ZERO(x)        __IF_X(x, _FLOAT_ZERO)            //float 判断x是否为零是返回true
    #define EQ_FLOAT(x, y)            EQ(x, y, _FLOAT_ZERO)            //判断表达式x与y是否相等
    
    // 3.2 double判断定义的宏
    #define _DOUBLE_ZERO            (0.000000000001)                //double 0误差判断值
    #define EQ_DOUBLE_ZERO(x)        __IF_X(x, _DOUBLE_ZERO)            //double 判断x是否为零是返回true
    #define EQ_DOUBLE(x,y)            EQ(x, y, _DOUBLE_ZERO)            //判断表达式x与y是否相等
    
    // 4.0 控制台打印错误信息, fmt必须是双引号括起来的宏
    #ifndef CERR
    #define CERR(fmt, ...) 
        fprintf(stderr,"[%s:%s:%d][error %d:%s]" fmt "
    ",
             __FILE__, __func__, __LINE__, errno, strerror(errno), ##__VA_ARGS__)
    #endif // !CERR
    
    // 4.1 控制台打印错误信息并退出, t同样fmt必须是 ""括起来的字符串常量
    #ifndef CERR_EXIT
    #define CERR_EXIT(fmt,...) 
        CERR(fmt, ##__VA_ARGS__),exit(EXIT_FAILURE)
    #endif // !CERR_EXIT
    
    // 4.2 执行后检测,如果有错误直接退出
    #ifndef IF_CHECK
    #define IF_CHECK(code) 
        if((code) < 0) 
            CERR_EXIT(#code)
    #endif // !IF_CHECK
    
    // 5.0 获取数组长度,只能是数组类型或""字符串常量,后者包含''
    #ifndef LEN
    #define LEN(arr) 
        (sizeof(arr) / sizeof(*(arr)))
    #endif/* !ARRLEN */
    
    // 7.0 置空操作
    #ifndef BZERO
    // v必须是个变量
    #define BZERO(v) 
        memset(&(v), 0, sizeof(v))
    #endif/* !BZERO */    
    
    // 9.0 scanf 健壮的
    #ifndef SAFETY_SCANF
    #define _STR_SAFETY_SCANF "Input error, please according to the prompt!"
    #define SAFETY_SCANF(scanf_code, ...) 
        while(printf(__VA_ARGS__), scanf_code){
            while('
    ' != getchar()) 
                ;
            puts(_STR_SAFETY_SCANF);
        }
        while('
    ' != getchar())
    #endif /*!SAFETY_SCANF*/
    
    // 简单的time帮助宏
    #ifndef TIME_PRINT
    #define _STR_TIME_PRINT "The current code block running time:%lf seconds
    "
    #define TIME_PRINT(code) 
        do{
            clock_t __st, __et;
            __st=clock();
            code
            __et=clock();
            printf(_STR_TIME_PRINT, (0.0 + __et - __st) / CLOCKS_PER_SEC);
        } while(0)
    #endif // !TIME_PRINT
    
    /*
     * 10.0 这里是一个 在 DEBUG 模式下的测试宏
     *
     * 用法 :
     * DEBUG_CODE({
     *        puts("debug start...");
     * });
     */
    #ifndef DEBUG_CODE
    # ifdef _DEBUG
    #    define DEBUG_CODE(code) code
    # else
    #    define DEBUG_CODE(code) 
    # endif    //    ! _DEBUG
    #endif    //    ! DEBUG_CODE
    
    //11.0 等待的宏 是个单线程没有加锁 | "请按任意键继续. . ."
    #define _STR_PAUSEMSG "Press any key to continue . . ."
    extern void sh_pause(void);
    #ifndef INIT_PAUSE
    
    #    ifdef _DEBUG
    #        define INIT_PAUSE() atexit(sh_pause)
    #    else
    #        define INIT_PAUSE()    /* 别说了,都重新开始吧 */
    #    endif
    
    #endif // !INIT_PAUSE
    
    //12.0 判断是大端序还是小端序,大端序返回true
    extern bool sh_isbig(void);
    
    /**
    *    sh_free - 简单的释放内存函数,对free再封装了一下
    **可以避免野指针
    **pobj:指向待释放内存的指针(void*)
    **/
    extern void sh_free(void ** pobj);
    
    /*
     * 比较两个结构体栈上内容是否相等,相等返回true,不等返回false
     * a    : 第一个结构体值
     * b    : 第二个结构体值
     *        : 相等返回true, 否则false
     */
    #define STRUCTCMP(a, b) 
        (!memcmp(&a, &b, sizeof(a)))
    
    #endif// ! _H_SIMPLEC_SCHEAD
    View Code

     schead.c

    #include <schead.h>
    
    //简单通用的等待函数
    inline void
    sh_pause(void) {
        rewind(stdin);
        printf(_STR_PAUSEMSG);
        sh_getch();
    }
    
    //12.0 判断是大端序还是小端序,大端序返回true
    inline bool
    sh_isbig(void) {
        static union {
            unsigned short _s;
            unsigned char _c;
        } __u = { 1 };
        return __u._c == 0;
    }
    
    /**
    *    sh_free - 简单的释放内存函数,对free再封装了一下
    **可以避免野指针
    **@pobj:指向待释放内存的指针(void*)
    **/
    void
    sh_free(void ** pobj) {
        if (pobj == NULL || *pobj == NULL)
            return;
        free(*pobj);
        *pobj = NULL;
    }
    
    // 为linux扩展一些功能
    #if defined(__GNUC__)
    
    /*
     * 得到用户输入的一个字符
     *        : 返回得到字符
     */
    int 
    sh_getch(void) {
        int cr;
        struct termios nts, ots;
    
        if (tcgetattr(0, &ots) < 0) // 得到当前终端(0表示标准输入)的设置
            return EOF;
    
        nts = ots;
        cfmakeraw(&nts); // 设置终端为Raw原始模式,该模式下所有的输入数据以字节为单位被处理
        if (tcsetattr(0, TCSANOW, &nts) < 0) // 设置上更改之后的设置
            return EOF;
    
        cr = getchar();
        if (tcsetattr(0, TCSANOW, &ots) < 0) // 设置还原成老的模式
            return EOF;
    
        return cr;
    }
    
    #endif
    View Code

    scalloc.h

    #ifndef _H_SIMPLEC_SCALLOC
    #define _H_SIMPLEC_SCALLOC
    
    #include <stdlib.h>
    
    // 释放sm_malloc_和sm_realloc_申请的内存, 必须配套使用
    void sm_free_(void * ptr, const char * file, int line, const char * func);
    // 返回申请的一段干净的内存
    void * sm_malloc_(size_t sz, const char * file, int line, const char * func);
    // 返回重新申请的内存, 只能和sm_malloc_配套使用
    void * sm_realloc_(void * ptr, size_t sz, const char * file, int line, const char * func);
    
    /*
     * 释放申请的内存
     * ptr    : 申请的内存
     */
    #define sm_free(ptr)        sm_free_(ptr, __FILE__, __LINE__, __func__)
    /*
     * 返回申请的内存, 并且是填充''
     * sz    : 申请内存的长度
     */
    #define sm_malloc(sz)        sm_malloc_(sz, __FILE__, __LINE__, __func__)
    /*
     * 返回申请到num*sz长度内存, 并且是填充''
     * num    : 申请的数量
     * sz    : 申请内存的长度
     */
    #define sm_calloc(num, sz)    sm_malloc_(num*sz, __FILE__, __LINE__, __func__)
    /*
     * 返回重新申请的内存
     * ptr    : 申请的内存
     * sz    : 申请内存的长度
     */
    #define sm_realloc(ptr, sz)    sm_realloc_(ptr, sz, __FILE__, __LINE__, __func__)
    
    // 定义全局内存使用宏, 替换原有的malloc系列函数
    #ifndef _SIMPLEC_ALLOC_CLOSE
    #    define free            sm_free
    #    define malloc        sm_malloc
    #    define calloc        sm_calloc
    #    define realloc        sm_realloc
    #endif
    
    #endif // !_H_SIMPLEC_SCALLOC
    View Code

    scalloc.c

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    // 标识枚举
    typedef enum {
        HF_Alloc,
        HF_Free
    } header_e;
    
    // 每次申请内存的[16-24]字节额外消耗, 用于记录内存申请情况
    struct header {
        header_e flag;        // 当前内存使用的标识
        int line;            // 申请的文件行
        const char * file;    // 申请的文件名
        const char * func;    // 申请的函数名
    };
    
    // 内部使用的malloc, 返回内存会用''初始化
    void * 
    sm_malloc_(size_t sz, const char * file, int line, const char * func) {
        struct header * ptr = malloc(sz + sizeof(struct header));
        // 检查内存分配的结果
        if(NULL == ptr) {
            fprintf(stderr, "_header_get >%s:%d:%s< alloc error not enough memory start fail!
    ", file, line, func);
            exit(EXIT_FAILURE);
        }
    
        ptr->flag = HF_Alloc;
        ptr->line = line;
        ptr->file = file;
        ptr->func = func;
        memset(++ptr, 0, sz);
        return ptr;
    }
    
    // 得到申请内存的开头部分, 并检查
    static struct header * _header_get(void * ptr, const char * file, int line, const char * func) {
        struct header * node = (struct header *)ptr - 1;
        // 正常情况直接返回
        if(HF_Alloc != node->flag) {    
            // 异常情况, 内存多次释放, 和内存无效释放
            fprintf(stderr, "_header_get free invalid memony flag %d by >%s:%d:%s<
    ", node->flag, file, line, func);
            exit(EXIT_FAILURE);
        }
        return node;
    }
    
    // 内部使用的realloc
    void * 
    sm_realloc_(void * ptr, size_t sz, const char * file, int line, const char * func) {
        struct header * node , * buf;
        if(NULL == ptr)
            return sm_malloc_(sz, file, line, func);
        
        // 合理内存分割
        node = _header_get(ptr, file, line, func);
        node->flag = HF_Free;
        // 构造返回内存信息
        buf = realloc(node, sz + sizeof(struct header));
        buf->flag = HF_Alloc;
        buf->line = line;
        buf->file = file;
        buf->func = func;
    
        return buf + 1;
    }
    
    // 内部使用的free, 每次释放都会打印日志信息
    void 
    sm_free_(void * ptr, const char * file, int line, const char * func) {
        if(NULL !=  ptr) {
            // 得到内存地址, 并且标识一下, 开始释放
            struct header * node = _header_get(ptr, file, line, func);
            node->flag = HF_Free;
            free(node);
        }
    }
    View Code

    tstr.h

    #ifndef _H_SIMPLEC_TSTR
    #define _H_SIMPLEC_TSTR
    
    #include <schead.h>
    
    //------------------------------------------------简单字符串辅助操作----------------------------------
    
    /*
     * 主要采用jshash 返回计算后的hash值
     * 不冲突率在 80% 左右还可以, 不要传入NULL
     */
    extern unsigned tstr_hash(const char * str);
    
    /*
     * 这是个不区分大小写的比较函数
     * ls        : 左边比较字符串
     * rs        : 右边比较字符串
     *            : 返回 ls>rs => >0 ; ls = rs => 0 ; ls<rs => <0
     */
    extern int tstr_icmp(const char * ls, const char * rs);
    
    /*
     * 这个代码是 对 strdup 的再实现, 调用之后需要free 
     * str        : 待复制的源码内容
     *            : 返回 复制后的串内容
     */
    extern char * tstr_dup(const char * str);
    
    //------------------------------------------------简单文本字符串辅助操作----------------------------------
    
    #ifndef _STRUCT_TSTR
    #define _STRUCT_TSTR
    //简单字符串结构,并定义文本字符串类型tstring
    struct tstr {
        char * str;        //字符串实际保存的内容
        int len;        //当前字符串大小
        int size;        //字符池大小
    };
    typedef struct tstr * tstr_t;
    #endif // !_STRUCT_TSTR
    
    //文本串栈上创建内容,不想用那些技巧了,就这样吧
    #define TSTR_NEW(var) 
        struct tstr $__##var = { NULL, 0, 0 }, * var = &$__##var;
    #define TSTR_DELETE(var) 
        sm_free(var->str)
    
    /*
     * tstr_t 的创建函数, 会根据str创建一个 tstr_t 结构的字符串
     * str    : 待创建的字符串
     *        : 返回创建好的字符串,如果创建失败返回NULL
     */
    extern tstr_t tstr_new(const char * str);
    
    /*
     * tstr_t 析构函数
     * tstr : tstr_t字符串指针量
     */
    extern void tstr_delete(tstr_t tstr);
    
    /*
     *  向简单文本字符串tstr中添加 一个字符c
     * tstr : 简单字符串对象
     * c    : 待添加的字符
     */
    extern void tstr_append(tstr_t tstr, int c);
    
    /*
     *  向简单文本串中添加只读字符串 
     * tstr    : 文本串
     * str    : 待添加的素材串
     */
    extern void tstr_appends(tstr_t tstr, const char * str);
    
    /*
     * 复制tstr中内容,得到char *, 需要自己 free释放
     * 假如你要清空tstr_t字符串只需要 设置 len = 0.就可以了
     * tstr    : 待分配的字符串
     *        : 返回分配好的字符串首地址
     */
    extern char * tstr_dupstr(tstr_t tstr);
    
    //------------------------------------------------简单文件辅助操作----------------------------------
    
    /*
     * 简单的文件帮助类,会读取完毕这个文件内容返回,失败返回NULL.
     * 需要事后使用 tstr_delete(ret); 销毁这个字符串对象
     * path    : 文件路径
     *        : 返回创建好的字符串内容,返回NULL表示读取失败
     */
    extern tstr_t tstr_file_readend(const char * path);
    
    /*
     * 文件写入,没有好说的, 会返回 RT_SuccessBase | RT_ErrorParam | RT_ErrorFopen
     * path    : 文件路径
     * str    : 待写入的字符串
     *        : 返回操作的结果 见上面枚举
     */
    extern int tstr_file_writes(const char * path, const char * str);
    
    /*
     * 文件追加内容 会返回 RT_SuccessBase | RT_ErrorParam | RT_ErrorFopen
     * path    : 文件路径
     * str    : 待写入的字符串
     *        : 返回操作的结果 见上面枚举
     */
    extern int tstr_file_append(const char * path, const char * str);
    
    #endif // !_H_SIMPLEC_TSTR
    View Code

    tstr.c

    #include <tstr.h>
    
    /*
     * 主要采用jshash 返回计算后的hash值
     * 不冲突率在 80% 左右还可以, 不要传入NULL
     */
    unsigned 
    tstr_hash(const char * str) {
        unsigned i, h = (unsigned)strlen(str), sp = (h >> 5) + 1;
        unsigned char * ptr = (unsigned char *)str;
    
        for (i = h; i >= sp; i -= sp)
            h ^= ((h<<5) + (h>>2) + ptr[i-1]);
    
        return h ? h : 1;
    }
    
    /*
     * 这是个不区分大小写的比较函数
     * ls        : 左边比较字符串
     * rs        : 右边比较字符串
     *            : 返回 ls>rs => >0 ; ls = rs => 0 ; ls<rs => <0
     */
    int 
    tstr_icmp(const char * ls, const char * rs) {
        int l, r;
        if(!ls || !rs)
            return (int)(ls - rs);
        
        do {
            if((l=*ls++)>='a' && l<='z')
                l -= 'a' - 'A';
            if((r=*rs++)>='a' && r<='z')
                r -= 'a' - 'A';
        } while(l && l==r);
        
        return l - r;
    }
    
    /*
     * 这个代码是 对 strdup 的再实现, 调用之后需要free
     * str        : 待复制的源码内容
     *            : 返回 复制后的串内容
     */
    char * 
    tstr_dup(const char * str)
    {
        size_t len; 
        char * nstr;
        if (NULL == str)
            return NULL;
    
        len = sizeof(char) * (strlen(str) + 1);
        nstr = sm_malloc(len);
        // 返回最后结果
        return memcpy(nstr, str, len);
    }
    
    //------------------------------------------------简单文本字符串辅助操作----------------------------------
    
    /*
     * tstr_t 的创建函数, 会根据str创建一个 tstr_t 结构的字符串
     * str    : 待创建的字符串
     *        : 返回创建好的字符串,如果创建失败返回NULL
     */
    tstr_t 
    tstr_new(const char * str) {
        tstr_t tstr = sm_malloc(sizeof(struct tstr));
        tstr_appends(tstr, str);
        return tstr;
    }
    
    /*
     * tstr_t 析构函数
     * tstr : tstr_t字符串指针量
     */
    inline void 
    tstr_delete(tstr_t tstr) {
        if (tstr) {
            sm_free(tstr->str);
            sm_free(tstr);
        }
    }
    
    //文本字符串创建的度量值
    #define _INT_TSTRING (32)
    
    //简单分配函数,智力一定会分配内存的, len > size的时候调用这个函数
    static void _tstr_realloc(tstr_t tstr, int len)
    {
        int size = tstr->size;
        for (size = size < _INT_TSTRING ? _INT_TSTRING : size; size < len; size <<= 1)
            ;
        //分配内存
        tstr->str = sm_realloc(tstr->str, size);
        tstr->size = size;
    }
    
    /*
     *  向简单文本字符串tstr中添加 一个字符c
     * tstr : 简单字符串对象
     * c    : 待添加的字符
     */
    void 
    tstr_append(tstr_t tstr, int c) {
        //不做安全检查
        int len = tstr->len + 1 + 1; // c + '' 而len只指向 字符串strlen长度
    
        //需要的❀, 需要进行内存分配, 唯一损失
        if (len > tstr->size)
            _tstr_realloc(tstr, len);
    
        tstr->len = --len;
        tstr->str[len - 1] = c;
        tstr->str[len] = '';
    }
    
    /*
     *  向简单文本串中添加只读字符串
     * tstr    : 文本串
     * str    : 待添加的素材串
     *        : 返回状态码主要是 _RT_EP _RT_EM
     */
    void 
    tstr_appends(tstr_t tstr, const char * str) {
        int len;
        if (!tstr || !str || !*str)
            return;
    
        // 检查内存是否需要重新构建
        len = tstr->len + (int)strlen(str) + 1;
        if (len > tstr->size)
            _tstr_realloc(tstr, len);
    
        strcpy(tstr->str + tstr->len, str);
        tstr->len = len - 1;
    }
    
    /*
     * 复制tstr中内容,得到char *, 需要自己 free释放
     * 假如你要清空tstr_t字符串只需要 设置 len = 0.就可以了
     * tstr    : 待分配的字符串
     *        : 返回分配好的字符串首地址
     */
    char * 
    tstr_dupstr(tstr_t tstr) {
        char * str;
        if (!tstr || tstr->len <= 0)
            return NULL;
        
        //下面就可以复制了,采用最快的一种方式
        str = sm_malloc(tstr->len + 1);
        return memcpy(str, tstr->str, tstr->len + 1);
    }
    
    //------------------------------------------------简单文件辅助操作----------------------------------
    
    /*
     * 简单的文件帮助类,会读取完毕这个文件内容返回,失败返回NULL.
     * 需要事后使用 tstr_delete(ret); 销毁这个字符串对象
     * path    : 文件路径
     *        : 返回创建好的字符串内容,返回NULL表示读取失败
     */
    tstr_t 
    tstr_file_readend(const char * path) {
        int c;
        tstr_t tstr;
        FILE * txt = fopen(path, "r");
        if (NULL == txt) {
            CERR("fopen r %s is error!", path);
            return NULL;
        }
    
        //这里创建文本串对象
        tstr = tstr_new(NULL);
    
        //这里读取文本内容
        while ((c = fgetc(txt)) != EOF)
            tstr_append(tstr, c);
    
        fclose(txt);//很重要创建了就要释放,否则会出现隐藏的句柄bug
        return tstr;
    }
    
    int _tstr_file_writes(const char * path, const char * str, const char * mode) {
        FILE* txt;
        // 检查参数是否有问题
        if (!path || !*path || !str) {
            CERR("check is '!path || !*path || !str'");
            return RT_ErrorParam;
        }
    
        if ((txt = fopen(path, mode)) == NULL) {
            CERR("fopen mode = '%s', path = '%s' error!", mode, path);
            return RT_ErrorFopen;
        }
    
        //这里写入信息
        fputs(str, txt);
    
        fclose(txt);
        return RT_SuccessBase;
    }
    
    /*
     * 文件写入,没有好说的, 会返回 RT_SuccessBase | RT_ErrorParam | RT_ErrorFopen
     * path    : 文件路径
     * str    : 待写入的字符串
     *        : 返回操作的结果 见上面枚举
     */
    inline int
    tstr_file_writes(const char * path, const char * str) {
        return _tstr_file_writes(path, str, "wb");
    }
    
    /*
     * 文件追加内容 会返回 RT_SuccessBase | RT_ErrorParam | RT_ErrorFopen
     * path    : 文件路径
     * str    : 待写入的字符串
     *        : 返回操作的结果 见上面枚举
     */
    inline int 
    tstr_file_appends(const char * path, const char * str) {
        return _tstr_file_writes(path, str, "ab");
    }
    View Code

    scjson.h

    #ifndef _H_SIMPLEC_SCJSON
    #define _H_SIMPLEC_SCJSON
    
    #include <tstr.h>
    
    // json 中几种数据类型定义 , 对于C而言 最难的是看不见源码,而不是api复杂, 更不是业务复杂
    #define _CJSON_FALSE    (0)
    #define _CJSON_TRUE        (1)
    #define _CJSON_NULL        (2)
    #define _CJSON_NUMBER    (3)
    #define _CJSON_STRING    (4)
    #define _CJSON_ARRAY    (5)
    #define _CJSON_OBJECT    (6)
    
    #define _CJSON_ISREF    (256)        //set 时候用如果是引用就不释放了
    #define _CJSON_ISCONST    (512)        //set时候用, 如果是const char* 就不释放了
    
    struct cjson {
        struct cjson * next, * prev;
        struct cjson * child; // type == _CJSON_ARRAY or type == _CJSON_OBJECT 那么 child 就不为空
    
        int type;
        char * key;    // json内容那块的 key名称     
        char * vs;    // type == _CJSON_STRING, 是一个字符串     
        double vd;  // type == _CJSON_NUMBER, 是一个num值, ((int)c->vd) 转成int 或 bool
    };
    
    //定义cjson_t json类型
    typedef struct cjson * cjson_t;
    
    /*
     * 这个宏,协助我们得到 int 值 或 bool 值 
     * 
     * item : 待处理的目标cjson_t结点
     */
    #define cjson_getint(item) 
        ((int)((item)->vd))
    
    /*
     *  删除json串内容  
     *  c        : 待释放json_t串内容
     */
    extern void cjson_delete(cjson_t c);
    
    /*
     * 对json字符串解析返回解析后的结果
     * jstr        : 待解析的字符串
     */
    extern cjson_t cjson_newtstr(tstr_t str);
    
    /*
     *    将json文件解析成json内容返回. 需要自己调用 cjson_delete
     * path        : json串路径
     *            : 返回处理好的cjson_t 内容,失败返回NULL
     */
    extern cjson_t cjson_newfile(const char * path);
    
    /*
     * 根据 item当前结点的 next 一直寻找到 NULL, 返回个数. 推荐在数组的时候使用
     * array    : 待处理的cjson_t数组对象
     *            : 返回这个数组中长度
     */
    extern int cjson_getlen(cjson_t array);
    
    /*
     * 根据索引得到这个数组中对象
     * array    : 数组对象
     * idx        : 查找的索引 必须 [0,cjson_getlen(array)) 范围内
     *            : 返回查找到的当前对象
     */
    extern cjson_t cjson_getarray(cjson_t array, int idx);
    
    /*
     * 根据key得到这个对象 相应位置的值
     * object    : 待处理对象中值
     * key        : 寻找的key
     *            : 返回 查找 cjson_t 对象
     */
    extern cjson_t cjson_getobject(cjson_t object, const char * key);
    
    
    // --------------------------------- 下面是 cjson 输出部分的处理代码 -----------------------------------------
    
    /*
     *  这里是将 cjson_t item 转换成字符串内容,需要自己free 
     * item        : cjson的具体结点
     *            : 返回生成的item的json串内容
     */
    extern char* cjson_print(cjson_t item);
    
    // --------------------------------- 下面是 cjson 输出部分的辅助代码 -----------------------------------------
    
    /*
     * 创建一个bool的对象 b==0表示false,否则都是true, 需要自己释放 cjson_delete
     * b        : bool 值 最好是 _Bool
     *            : 返回 创建好的json 内容
     */
    extern cjson_t cjson_newnull();
    extern cjson_t cjson_newbool(int b);
    extern cjson_t cjson_newnumber(double vd);
    extern cjson_t cjson_newstring(const char * vs);
    extern cjson_t cjson_newarray(void);
    extern cjson_t cjson_newobject(void);
    
    /*
     * 按照类型,创建 对映类型的数组 cjson对象
     *目前支持 _CJSON_NULL _CJSON_BOOL/FALSE or TRUE , _CJSON_NUMBER, _CJSON_STRING
     * NULL => array 传入NULL, FALSE 使用char[],也可以传入NULL, NUMBER 只接受double, string 只接受char**
     * type        : 类型目前支持 上面几种类型
     * array    : 数组原始数据
     * len        : 数组中元素长度
     *            : 返回创建的数组对象
     */
    extern cjson_t cjson_newtypearray(int type, const void * array, int len);
    
    /*
     * 在array中分离第idx个索引项内容.
     * array    : 待处理的json_t 数组内容
     * idx        : 索引内容
     *            : 返回分离的json_t内容
     */
    extern cjson_t cjson_detacharray(cjson_t array, int idx);
    
    /*
     * 在object json 中分离 key 的项出去
     * object    : 待分离的对象主体内容
     * key        : 关联的键
     *            : 返回分离的 object中 key的项json_t
     */
    extern cjson_t cjson_detachobject(cjson_t object, const char * key);
    
    #endif // !_H_SIMPLEC_SCJSON
    View Code

    scjson.c

    #include <scjson.h>
    #include <float.h>
    #include <limits.h>
    #include <math.h>
    
    // 删除cjson
    static void _cjson_delete(cjson_t c) {
        cjson_t next;
        while (c) {
            next = c->next;
            //递归删除儿子
            if (!(c->type & _CJSON_ISREF)) {
                if (c->child) //如果不是尾递归,那就先递归
                    _cjson_delete(c->child);
                if (c->vs)
                    sm_free(c->vs);
            }
            else if (!(c->type & _CJSON_ISCONST) && c->key)
                sm_free(c->key);
            sm_free(c);
            c = next;
        }
    }
    
    /*
    *  删除json串内容,最近老是受清华的老学生打击, 会起来的......
    *  c        : 待释放json_t串内容
    */
    inline void 
    cjson_delete(cjson_t c) {
        if (NULL == c)
            return;
        _cjson_delete(c);
    }
    
    //构造一个空 cjson 对象
    static inline cjson_t _cjson_new(void) {
        return sm_malloc(sizeof(struct cjson));
    }
    
    // 简化的代码段,用宏来简化代码书写 , 16进制处理
    #define __parse_hex4_code(c, h) 
        if (c >= '0' && c <= '9') 
            h += c - '0'; 
        else if (c >= 'A' && c <= 'F') 
            h += 10 + c - 'A'; 
        else if (c >= 'a' && c <= 'z') 
            h += 10 + c - 'F'; 
        else 
            return 0
    
    // 等到unicode char代码
    static unsigned _parse_hex4(const char * str) {
        unsigned h = 0;
        char c = *str;
        //第一轮
        __parse_hex4_code(c, h);
        h <<= 4;
        c = *++str;
        //第二轮
        __parse_hex4_code(c, h);
        h <<= 4;
        c = *++str;
        //第三轮
        __parse_hex4_code(c, h);
        h <<= 4;
        c = *++str;
        //第四轮
        __parse_hex4_code(c, h);
    
        return h;
    }
    
    // 分析字符串的子函数,
    static const char* _parse_string(cjson_t item, const char * str) {
        static unsigned char _marks[] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
        const char * ptr;
        char * nptr, * out;
        char c;
        int len;
        unsigned uc, nuc;
    
        if (*str != '"') { // 检查是否是字符串内容
            CERR("need \" str => %s error!", str);
            return NULL;
        }
    
        for (ptr = str + 1, len = 0; (c = *ptr++) != '"' && c; ++len)
            if (c == '\') //跳过转义字符
                ++ptr;
        out = sm_malloc(len + 1);
        // 这里复制拷贝内容
        for (ptr = str + 1, nptr = out; (c = *ptr) != '"' && c; ++ptr) {
            if (c != '\') {
                *nptr++ = c;
                continue;
            }
            // 处理转义字符
            switch ((c = *++ptr)) {
            case 'b': *nptr++ = ''; break;
            case 'f': *nptr++ = 'f'; break;
            case 'n': *nptr++ = '
    '; break;
            case 'r': *nptr++ = '
    '; break;
            case 't': *nptr++ = '	'; break;
            case 'u': // 将utf16 => utf8, 专门的utf处理代码
                uc = _parse_hex4(ptr + 1);
                ptr += 4;//跳过后面四个字符, unicode
                if ((uc >= 0xDC00 && uc <= 0xDFFF) || uc == 0)    break;    /* check for invalid.    */
    
                if (uc >= 0xD800 && uc <= 0xDBFF) { /* UTF16 surrogate pairs.    */
                    if (ptr[1] != '\' || ptr[2] != 'u')    
                        break;    /* missing second-half of surrogate.    */
                    nuc = _parse_hex4(ptr + 3);
                    ptr += 6;
                    if (nuc < 0xDC00 || nuc>0xDFFF)        
                        break;    /* invalid second-half of surrogate.    */
                    uc = 0x10000 + (((uc & 0x3FF) << 10) | (nuc & 0x3FF));
                }
    
                len = 4;
                if (uc < 0x80) 
                    len = 1;
                else if (uc < 0x800) 
                    len = 2;
                else if (uc < 0x10000) 
                    len = 3; 
                nptr += len;
    
                switch (len) {
                case 4: *--nptr = ((uc | 0x80) & 0xBF); uc >>= 6;
                case 3: *--nptr = ((uc | 0x80) & 0xBF); uc >>= 6;
                case 2: *--nptr = ((uc | 0x80) & 0xBF); uc >>= 6;
                case 1: *--nptr = (uc | _marks[len]);
                }
                nptr += len;
                break;
            default: *nptr++ = c;
            }
        }
    
        *nptr = '';
        if (c == '"')
            ++ptr;
        item->vs = out;
        item->type = _CJSON_STRING;
        return ptr;
    }
    
    // 分析数值的子函数,写的可以
    static const char * _parse_number(cjson_t item, const char * str) {
        double n = 0.0, ns = 1.0, nd = 0.0; //n把偶才能值, ns表示开始正负, 负为-1, nd 表示小数后面位数
        int e = 0, es = 1; //e表示后面指数, es表示 指数的正负,负为-1
        char c;
    
        if ((c = *str) == '-' || c == '+') {
            ns = c == '-' ? -1.0 : 1.0; //正负号检测, 1表示负数
            ++str;
        }
        //处理整数部分
        for (c = *str; c >= '0' && c <= '9'; c = *++str)
            n = n * 10 + c - '0';
        if (c == '.')
            for (; (c = *++str) >= '0' && c <= '9'; --nd)
                n = n * 10 + c - '0';
    
        // 处理科学计数法
        if (c == 'e' || c == 'E') {
            if ((c = *++str) == '+') //处理指数部分
                ++str;
            else if (c == '-')
                es = -1, ++str;
            for (; (c = *str) >= '0' && c <= '9'; ++str)
                e = e * 10 + c - '0';
        }
    
        //返回最终结果 number = +/- number.fraction * 10^+/- exponent
        n = ns * n * pow(10.0, nd + es * e);
        item->vd = n;
        item->type = _CJSON_NUMBER;
        return str;
    }
    
    // 递归下降分析 需要声明这些函数
    static const char * _parse_array(cjson_t item, const char * str);
    static const char * _parse_object(cjson_t item, const char * str);
    static const char * _parse_value(cjson_t item, const char * value);
    
    // 分析数组的子函数, 采用递归下降分析
    static const char * _parse_array(cjson_t item, const char * str) {
        cjson_t child;
        if (*str != '[') {
            CERR("array str error start: %s.", str);
            return NULL;
        }
    
        item->type = _CJSON_ARRAY;
        str = str + 1;
        if (*str == ']') // 低估提前结束
            return str + 1;
    
        item->child = child = _cjson_new();
        str = _parse_value(child, str);
        if (NULL == str) { // 解析失败 直接返回
            CERR("array str error e n d one: %s.", str);
            return NULL;
        }
        while (*str == ',') {
            cjson_t nitem = _cjson_new();
            child->next = nitem;
            nitem->prev = child;
            child = nitem;
            str = _parse_value(child, str + 1);
            if (NULL == str) {// 写代码是一件很爽的事
                CERR("array str error e n d two: %s.", str);
                return NULL;
            }
        }
    
        if (*str != ']') {
            CERR("array str error e n d: %s.", str);
            return NULL;
        }
        return str + 1; // 跳过']'
    }
    
    // 分析对象的子函数
    static const char * _parse_object(cjson_t item, const char * str) {
        cjson_t child;
        if (*str != '{') {
            CERR("object str error start: %s.", str);
            return NULL;
        }
    
        item->type = _CJSON_OBJECT;
        str = str + 1;
        if (*str == '}')
            return str + 1;
    
        //处理结点, 开始读取一个 key
        item->child = child = _cjson_new();
        str = _parse_string(child, str);
        if (!str || *str != ':') {
            CERR("_skip _parse_string is error : %s!", str);
            return NULL;
        }
        child->key = child->vs;
        child->vs = NULL;
    
        str = _parse_value(child, str + 1);
        if (!str) {
            CERR("_parse_object _parse_value is error 2!");
            return NULL;
        }
    
        // 递归解析
        while (*str == ',') {
            cjson_t nitem = _cjson_new();
            child->next = nitem;
            nitem->prev = child;
            child = nitem;
            str = _parse_string(child, str + 1);
            if (!str || *str != ':'){
                CERR("_parse_string need name or no equal ':' %s.", str);
                return NULL;
            }
            child->key = child->vs;
            child->vs = NULL;
    
            str = _parse_value(child, str+1);
            if (!str) {
                CERR("_parse_string need item two ':' %s.", str);
                return NULL;
            }
        }
    
        if (*str != '}') {
            CERR("object str error e n d: %s.", str);
            return NULL;
        }
        return str + 1;
    }
    
    // 将value 转换塞入 item json值中一部分
    static const char * _parse_value(cjson_t item, const char * value) {
        char c; 
        if ((value) && (c = *value)) {
            switch (c) {
                // n = null, f = false, t = true
            case 'n' : return item->type = _CJSON_NULL, value + 4;
            case 'f' : return item->type = _CJSON_FALSE, value + 5;
            case 't' : return item->type = _CJSON_TRUE, item->vd = 1.0, value + 4;
            case '"': return _parse_string(item, value);
            case '0' : case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
            case '+' : case '-': return _parse_number(item, value);
            case '[' : return _parse_array(item, value);
            case '{' : return _parse_object(item, value);
            }
        }
        // 循环到这里是意外 数据
        CERR("params value = %s!", value);
        return NULL;
    }
    
    /*
     * 对json字符串解析返回解析后的结果
     * jstr        : 待解析的字符串
     *            : 返回解析好的字符串内容
     */
    static cjson_t _cjson_parse(const char * jstr) {
        cjson_t c = _cjson_new();
        const char * end;
    
        if (!(end = _parse_value(c, jstr))) {
            CERR("_parse_value params end = %s!", end);
            cjson_delete(c);
            return NULL;
        }
    
        //这里是否检测 返回测试数据
        return c;
    }
    
    /*
     * 将 jstr中 不需要解析的字符串都去掉,并且纪念mini 比男的还平
     * jstr        : 待处理的json串
     *            : 返回压缩后的json串长度
     */
    static int _cjson_mini(char * jstr) {
        char c, *in = jstr, *to = jstr;
    
        while ((c = *to)) {
            // step 1 : 处理字符串
            if (c == '"') {
                *in++ = c;
                while ((c = *++to) && (c != '"' || to[-1] == '\'))
                    *in++ = c;
                if (c) {
                    *in++ = c;
                    ++to;
                }
                continue;
            }
    
            // step 2 : 处理不可见特殊字符
            if (c < '!') {
                ++to;
                continue;
            }
    
            if (c == '/') {
                // step 3 : 处理 // 解析到行末尾
                if (to[1] == '/') {
                    while ((c = *++to) && c != '
    ')
                        ;
                    continue;
                }
    
                // step 4 : 处理 /*
                if (to[1] == '*') {
                    while ((c = *++to) && (c != '*' || to[1] != '/'))
                        ;
                    if (c)
                        to += 2;
                    continue;
                }
            }
    
            // step 5 : 合法数据直接保存
            *in++ = *to++;
        }
    
        *in = '';
        return in - jstr;
    }
    
    /*
     * 对json字符串解析返回解析后的结果
     * jstr        : 待解析的字符串
     */
    inline cjson_t 
    cjson_newtstr(tstr_t str) {
        str->len = _cjson_mini(str->str);
        return _cjson_parse(str->str);
    }
    
    // 从json文件中解析出最简json数据
    static tstr_t _cjson_newfile(const char * path) {
        char c, n;
        tstr_t tstr;
        FILE * txt = fopen(path, "r");
        if (NULL == txt) {
            CERR("fopen r %s is error!", path);
            return NULL;
        }
    
        //这里创建文本串对象
        tstr = tstr_new(NULL);
    
        while ((c = fgetc(txt)) != EOF) {
            // step 1 : 处理字符串
            if (c == '"') {
                tstr_append(tstr, c);
                for (n = c; ((c = fgetc(txt)) != EOF) && (c != '"' || n == '\'); n = c)
                    tstr_append(tstr, c);
                if (EOF != c)
                    tstr_append(tstr, c);
                continue;
            }
    
            // step 2 : 处理不可见特殊字符
            if (c < '!')
                continue;
    
            if (c == '/') {
                // step 3 : 处理 // 解析到行末尾
                n = fgetc(txt);
                if (n == '/') {
                    while ((c = fgetc(txt)) != EOF && c != '
    ')
                        ;
                    continue;
                }
    
                // step 4 : 处理 /*
                if (n == '*') {
                    while ((c = fgetc(txt)) != EOF) {
                        if (c == '*') {
                            n = fgetc(txt);
                            if (n == '/')
                                break;
                            ungetc(n, txt);
                        }
                    }
                    continue;
                }
                ungetc(n, txt);
            }
    
            // step 5 : 合法数据直接保存
            tstr_append(tstr, c);
        }
    
        fclose(txt);//很重要创建了就要释放,否则会出现隐藏的句柄bug
        return tstr;
    }
    
    /*
     *    将json文件解析成json内容返回. 需要自己调用 cjson_delete
     * path        : json串路径
     *            : 返回处理好的cjson_t 内容,失败返回NULL
     */
    cjson_t
    cjson_newfile(const char * path) {
        cjson_t root;
        tstr_t tstr = _cjson_newfile(path);
        if (!tstr) {
            CERR("_cjson_dofile_tstr path:%s is error!", path);
            return NULL;
        }
        root = _cjson_parse(tstr->str);
        tstr_delete(tstr);
        return root;
    }
    
    /*
     * 根据 item当前结点的 next 一直寻找到 NULL, 返回个数. 推荐在数组的时候使用
     * array    : 待处理的cjson_t数组对象
     *            : 返回这个数组中长度
     */
    int 
    cjson_getlen(cjson_t array) {
        int len = 0;
        if (array) {
            for (array = array->child; array; array = array->next)
                ++len;
        }
        return len;
    }
    
    /*
     * 根据索引得到这个数组中对象
     * array    : 数组对象
     * idx        : 查找的索引 必须 [0,cjson_getlen(array)) 范围内
     *            : 返回查找到的当前对象
     */
    cjson_t 
    cjson_getarray(cjson_t array, int idx) {
        cjson_t c;
        DEBUG_CODE({
            if (!array || idx < 0) {
                CERR("array:%p, idx=%d params is error!", array, idx);
                return NULL;
            }
        });
    
        for (c = array->child; c&&idx > 0; c = c->next)
            --idx;
    
        return c;
    }
    
    /*
     * 根据key得到这个对象 相应位置的值
     * object    : 待处理对象中值
     * key        : 寻找的key
     *            : 返回 查找 cjson_t 对象
     */
    cjson_t 
    cjson_getobject(cjson_t object, const char* key) {
        cjson_t c;
        DEBUG_CODE({
            if (!object || !key || !*key) {
                CERR("object:%p, key=%s params is error!", object, key);
                return NULL;
            }
        });
    
        for (c = object->child; c && tstr_icmp(key, c->key); c = c->next)
            ;
    
        return c;
    }
    
    // --------------------------------- 下面是 cjson 输出部分的处理代码 -----------------------------------------
    
    // 2^n>=x , n是最小的整数
    static int _pow2gt(int x) {
        --x;
        x |= x >> 1;
        x |= x >> 2;
        x |= x >> 4;
        x |= x >> 8;
        x |= x >> 16;
        return x + 1;
    }
    
    /*
     *     这里使用 tstr_t 结构 size 这里表示 字符串总大小,没有变化
     * len 表示当前字符串的字符串起始偏移量 即 tstr_t->str + tstr_t->len 起始的
     */
    static char* _ensure(tstr_t p, int need) {
        char * nbuf;
        int nsize;
        if (!p || !p->str) {
            CERR("p:%p need:%d is error!", p, need);
            return NULL;
        }
        need += p->len;
        if (need <= p->size) //内存够用直接返回结果
            return p->str + p->len;
        nsize = _pow2gt(need);
        // 一定会成功, 否则一切都回归奇点
        nbuf = sm_malloc(nsize * sizeof(char));
        //这里复制内容
        memcpy(nbuf, p->str, p->size);
        sm_free(p->str);
        p->size = nsize;
        p->str = nbuf;
        return nbuf + p->len;
    }
    
    // 这里更新一下 当前字符串, 返回当前字符串的长度
    static inline int _update(tstr_t p) {
        return (!p || !p->str) ? 0 : p->len + (int)strlen(p->str+p->len);
    }
    
    // 将item 中值转换成字符串 保存到p中
    static char * _print_number(cjson_t item, tstr_t p) {
        char* str = NULL;
        double d = item->vd;
        int i = (int)d;
        
        if (d == 0) {  //普通0
            str = _ensure(p, 2);
            if (str)
                str[0] = '0', str[1] = '';
        }
        else if ((fabs(d - i)) <= DBL_EPSILON && d <= INT_MAX && d >= INT_MIN) {
            str = _ensure(p, 21); //int 值 
            if (str)
                sprintf(str, "%d", i);
        }
        else {
            str = _ensure(p, 64); //double值 
            if (str) {
                double nd = fabs(d); //得到正值开始比较
                if(fabs(floor(d) - d) <= DBL_EPSILON && nd < 1.0e60)
                    sprintf(str, "%.0f", d);
                else if(nd < 1.0e-6 || nd > 1.0e9) //科学计数法
                    sprintf(str, "%e", d);
                else
                    sprintf(str, "%f", d);
    
            }
        }
    
        return str;
    }
    
    // 输出字符串内容
    static char * _print_string(char * str, tstr_t p) {
        const char * ptr;
        char * nptr, * out;
        int len = 0, flag = 0;
        unsigned char c;
    
        if (!str || !*str) { //最特殊情况,什么都没有 返回NULL
            out = _ensure(p, 3);
            if (!out)
                return NULL;
            out[0] = '"', out[1] = '"', out[2] = '';
            return out;
        }
    
        for (ptr = str; (c=*ptr); ++ptr)
            flag |= ((c > 0 && c < 32) || c == '"' || c == '\');
        
        if (!flag) {  //没有特殊字符直接处理结果
            len = (int)(ptr - str);
            out = _ensure(p,len + 3);
            if (!out)
                return NULL;
            nptr = out;
            *nptr++ = '"';
            strcpy(nptr, str);
            nptr[len] = '"';
            nptr[len + 1] = '';
            return out;
        }
    
        //处理 存在 "和转义字符内容
        for (ptr = str; (c = *ptr) && ++len; ++ptr) {
            if (strchr(""\f
    
    	", c))
                ++len;
            else if (c < 32) //隐藏字符的处理, 这里可以改
                len += 5;
        }
    
        if ((out = _ensure(p, len + 3)) == NULL)
            return NULL;
        //先添加 "
        nptr = out;
        *nptr++ = '"';
        for (ptr = str; (c = *ptr); ++ptr) {
            if (c > 31 && c != '"' && c != '\') {
                *nptr++ = c;
                continue;
            }
            *nptr++ = '\';
            switch (c){
            case '\':    *nptr++ = '\';    break;
            case '"':    *nptr++ = '"';    break;
            case '':    *nptr++ = 'b';    break;
            case 'f':    *nptr++ = 'f';    break;
            case '
    ':    *nptr++ = 'n';    break;
            case '
    ':    *nptr++ = 'r';    break;
            case '	':    *nptr++ = 't';    break;
            default: sprintf(nptr, "u%04x", c);nptr += 5;    /* 不可见字符 采用 4字节字符编码 */
            }
        }
        *nptr++ = '"';
        *nptr = '';
        return out;
    }
    
    //这里是 递归下降 的函数声明处, 分别是处理值, 数组, object
    static char * _print_value(cjson_t item, tstr_t p);
    static char * _print_array(cjson_t item, tstr_t p);
    static char * _print_object(cjson_t item, tstr_t p);
    
    // 定义实现部分, 内部私有函数 认为 item 和 p都是存在的
    static char * _print_value(cjson_t item, tstr_t p) {
        char * out = NULL;
        switch ((item->type) & UCHAR_MAX) { // 0xff
        case _CJSON_FALSE: if ((out = _ensure(p, 6))) strcpy(out, "false"); break;
        case _CJSON_TRUE: if ((out = _ensure(p, 5))) strcpy(out, "true"); break;
        case _CJSON_NULL: if ((out = _ensure(p, 5))) strcpy(out, "null"); break;
        case _CJSON_NUMBER:    out = _print_number(item, p); break;
        case _CJSON_STRING:    out = _print_string(item->vs, p); break;
        case _CJSON_ARRAY:    out = _print_array(item, p); break;
        case _CJSON_OBJECT:    out = _print_object(item, p); break;
        }
    
        return out;
    }
    
    // 同样 假定 item 和 p都是存在且不为NULL
    static char * _print_array(cjson_t item, tstr_t p)
    {
        char * ptr;
        cjson_t child = item->child;
        int ncut, i;
        // 得到孩子结点的深度
        for (ncut = 0; (child); child = child->child)
            ++ncut;
        if (!ncut) { //没有孩子结点 直接空数组返回结果
            char* out = NULL;
            if (!(out = _ensure(p, 3))) 
                strcpy(out, "[]");
            return out;
        }
    
        i = p->len;
        if (!(ptr = _ensure(p, 1)))
            return NULL;
        *ptr = '[';
        ++p->len;
        for (child = item->child; (child); child = child->next) {
            _print_value(child, p);
            p->len = _update(p);
            if (child->next) {
                if (!(ptr = _ensure(p, 2)))
                    return NULL;
                *ptr++ = ',';
                *ptr = '';
                p->len += 1;
            }
        }
        if (!(ptr = _ensure(p, 2)))
            return NULL;
        *ptr++ = ']';
        *ptr = '';
        return p->str + i;
    
    }
    
    // 同样 假定 item 和 p都是存在且不为NULL, 相信这些代码是安全的
    static char * _print_object(cjson_t item, tstr_t p)
    {
        char * ptr;
        int i, ncut, len;
        cjson_t child = item->child;
    
        // 得到孩子结点的深度
        for (ncut = 0; child; child = child->child)
            ++ncut;
        if (!ncut) {
            char* out = NULL;
            if (!(out = _ensure(p, 3)))
                strcpy(out, "{}");
            return out;
        }
    
        i = p->len;
        if (!(ptr = _ensure(p, 2)))
            return NULL;
        *ptr++ = '{';
        *ptr -= '';
        p->len += 1;
        // 根据子结点 处理
        for (child = item->child; (child); child = child->next) {
            _print_string(child->key, p);
            p->len = _update(p);
    
            //加入一个冒号
            if (!(ptr = _ensure(p, 1)))
                return NULL;
            *ptr++ = ':';
            p->len += 1;
    
            //继续打印一个值
            _print_value(child, p);
            p->len = _update(p);
    
            //结算最后内容
            len = child->next ? 1 : 0;
            if ((ptr = _ensure(p, len + 1)) == NULL)
                return NULL;
            if (child->next)
                *ptr++ = ',';
            *ptr = '';
            p->len += len;
        }
        if (!(ptr = _ensure(p, 2)))
            return NULL;
        *ptr++ = '}';
        *ptr = '';
        return p->str + i;
    }
    
    #define _INT_CJONSTR    (256)
    /*
     *  这里是将 cjson_t item 转换成字符串内容,需要自己free
     * item        : cjson的具体结点
     *            : 返回生成的item的json串内容
     */
    char * 
    cjson_print(cjson_t item) {
        struct tstr p;
        char * out;
        if (NULL == item) {
            CERR("item is error = %p!", item);
            return NULL;
        }
        // 构建内存
        p.str = sm_malloc(sizeof(char) * _INT_CJONSTR);
        p.size = _INT_CJONSTR;
        p.len = 0;
    
        out = _print_value(item, &p); //从值处理开始, 返回最终结果
        if (out == NULL) {
            sm_free(p.str);
            CERR("_print_value item:%p, p:%p is error!", item, &p);
            return NULL;
        }
        return sm_realloc(out, strlen(out) + 1); // 体积变小 realloc返回一定成功
    }
    
    // --------------------------------- 下面是 cjson 输出部分的辅助代码 -----------------------------------------
    
    /*
     * 创建一个bool的对象 b==0表示false,否则都是true, 需要自己释放 cjson_delete
     * b        : bool 值 最好是 _Bool
     *            : 返回 创建好的json 内容
     */
    inline cjson_t
    cjson_newnull() {
        cjson_t item = _cjson_new();
        item->type = _CJSON_NULL; 
        return item;
    }
    
    inline cjson_t
    cjson_newbool(int b) {
        cjson_t item = _cjson_new();
        item->vd = item->type = b ? _CJSON_TRUE : _CJSON_FALSE; 
        return item;
    }
    
    inline cjson_t
    cjson_newnumber(double vd)
    {
        cjson_t item = _cjson_new();
        item->type = _CJSON_NUMBER;
        item->vd = vd;
        return item;
    }
    
    inline cjson_t
    cjson_newstring(const char* vs)
    {
        cjson_t item = _cjson_new();
        item->type = _CJSON_STRING;
        item->vs = tstr_dup(vs);
        return item;
    }
    
    inline cjson_t
    cjson_newarray(void)
    {
        cjson_t item = _cjson_new();
        item->type = _CJSON_ARRAY;
        return item;
    }
    
    inline cjson_t
    cjson_newobject(void)
    {
        cjson_t item = _cjson_new();
        item->type = _CJSON_OBJECT;
        return item;
    }
    
    /*
     * 按照类型,创建 对映类型的数组 cjson对象
     *目前支持 _CJSON_NULL _CJSON_BOOL/FALSE or TRUE , _CJSON_NUMBER, _CJSON_STRING
     * NULL => array 传入NULL, FALSE 使用char[],也可以传入NULL, NUMBER 只接受double, string 只接受char**
     * type        : 类型目前支持 上面几种类型
     * array    : 数组原始数据
     * len        : 数组中元素长度
     *            : 返回创建的数组对象
     */
    cjson_t 
    cjson_newtypearray(int type, const void * array, int len) {
        int i;
        cjson_t n = NULL, p = NULL, a;
        // _DEBUG 模式下简单检测一下
        DEBUG_CODE({
            if(type < _CJSON_FALSE || type > _CJSON_STRING || len <=0){
                CERR("check param is error! type = %d, len = %d.", type, len);
                return NULL;
            }
        });
        
        // 这里是实际执行代码
        a = cjson_newarray();
        for(i=0; i<len; ++i){
            switch(type){
            case _CJSON_NULL: n = cjson_newnull(); break;
            case _CJSON_FALSE: n = cjson_newbool(array? ((char*)array)[i] : 0); break;
            case _CJSON_TRUE: n = cjson_newbool(array? ((char*)array)[i] : 1); break;
            case _CJSON_NUMBER: n = cjson_newnumber(((double*)array)[i]); break;
            case _CJSON_STRING: n = cjson_newstring(((char**)array)[i]);break;
            }
            if(i){ //有你更好
                p->next = n;
                n->prev = p;
            }
            else
                a->child = n;
            p = n;
        }
        return a;
    }
    
    /*
     * 在array中分离第idx个索引项内容.
     * array    : 待处理的json_t 数组内容
     * idx        : 索引内容
     *            : 返回分离的json_t内容
     */
    cjson_t 
    cjson_detacharray(cjson_t array, int idx) {
        cjson_t c;
        DEBUG_CODE({
            if(!array || idx<0){
                CERR("check param is array:%p, idx:%d.", array, idx);
                return NULL;
            }
        });
        
        for(c=array->child; idx>0 && c; c = c->next)
            --idx;
        if(c>0){
            CERR("check param is too dig idx:sub %d.", idx);
            return NULL;
        }
        //这里开始拼接了
        if(c->prev)
            c->prev->next = c->next;
        if(c->next)
            c->next->prev = c->prev;
        if(c == array->child)
            array->child = c->next;
        c->prev = c->next = NULL;
        return c;
    }
    
    /*
     * 在object json 中分离 key 的项出去
     * object    : 待分离的对象主体内容
     * key        : 关联的键
     *            : 返回分离的 object中 key的项json_t
     */
    cjson_t 
    cjson_detachobject(cjson_t object, const char * key) {
        cjson_t c;
        DEBUG_CODE({
            if(!object || !object->child || !key || !*key){
                CERR("check param is object:%p, key:%s.", object, key);
                return NULL;
            }
        });
        
        for(c=object->child; c && tstr_icmp(c->key, key); c=c->next)
            ;
        if(!c) {
            CERR("check param key:%s => vlaue is empty.", key);
            return NULL;
        }
        if(c->prev)
            c->prev->next = c->next;
        if(c->next)
            c->next->prev = c->prev;
        if(c == object->child)
            object->child = c->next;
        c->prev = c->next = NULL;
        return c;
    }
    View Code

    主要测试文件 test_cjson.c

    #include <schead.h>
    #include <scjson.h>
    
    // 测试 cjson 函数
    void test_readstr(void) {
        // 第二个 测试 json 串的解析
        puts("测试 cjson 是否可用");
        char text1[] = "{
    "name": "Jack (\"Bee\") Nimble", 
    "format": {"type":       "rect", 
    "width":      1920, 
    "height":     1080, 
    "interlace":  false,"frame rate": 24
    }
    }";
        TSTR_NEW(jstr1);
        jstr1->str = text1;
        cjson_t js = cjson_newtstr(jstr1);
    
        cjson_t name = cjson_getobject(js, "name");
        printf("name => %s
    ", name->vs);
    
        cjson_t format = cjson_getobject(js, "format");
        printf("len(format) => %d
    ", cjson_getlen(format));
    
        cjson_t interlace = cjson_getobject(format, "interlace");
        printf("interlace => %d
    ", cjson_getint(interlace));
    
        cjson_delete(js);
    
        //进行第三组测试
    
        puts(" 测试 数组的读取");
        char text2[] = "["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]";
        TSTR_NEW(jstr2);
        jstr2->str = text2;
        js = cjson_newtstr(jstr2);
        int len = cjson_getlen(js);
        int i;
        for (i = 0; i < len; ++i) {
            cjson_t item = cjson_getarray(js,i);
            printf("%d => %s.
    ", i, item->vs);
        }
        cjson_delete(js);
    
    
        puts("第三组测试");
        char text3[] = "[
        [0, -1, 0],
        [1, 0, 0],
        [0, 0, 1]
        ]
    ";
        TSTR_NEW(jstr3);
        jstr3->str = text3;
        js = cjson_newtstr(jstr3);
        len = cjson_getlen(js);
        for (i = 0; i < len; ++i) {
            cjson_t item = cjson_getarray(js, i);
            printf("%d => %d.
    ", i, cjson_getlen(item));
        }
    
        cjson_delete(js);
        exit(EXIT_SUCCESS);
    }
    
    /*
     * simple c 框架业务层启动的代码
     */
    void test_readfile(void) {
    
        // 测试json解析结果是否正常
        cjson_t goods = cjson_newfile("./goods.json");
    
        // 数据输出
        int len = cjson_getlen(goods);
        printf("len = %d
    ", len);
    
        // 打印其中一个数据
        int idx = len / 2;
        cjson_t jsidx = cjson_getarray(goods, idx);
        int ilen = cjson_getlen(jsidx);
        printf("ilen = %d
    ", ilen);
        printf("["%s", "%s", %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d]
    ",
            cjson_getarray(jsidx, 0)->vs, 
            cjson_getarray(jsidx, 1)->vs,
            cjson_getint(cjson_getarray(jsidx, 2)),
            cjson_getint(cjson_getarray(jsidx, 3)),
            cjson_getint(cjson_getarray(jsidx, 4)),
            cjson_getint(cjson_getarray(jsidx, 5)),
            cjson_getint(cjson_getarray(jsidx, 6)),
            cjson_getint(cjson_getarray(jsidx, 7)),
            cjson_getint(cjson_getarray(jsidx, 8)),
            cjson_getint(cjson_getarray(jsidx, 9)),
            cjson_getint(cjson_getarray(jsidx, 10)),
            cjson_getint(cjson_getarray(jsidx, 11)),
            cjson_getint(cjson_getarray(jsidx, 12)),
            cjson_getint(cjson_getarray(jsidx, 13)),
            cjson_getint(cjson_getarray(jsidx, 14)),
            cjson_getint(cjson_getarray(jsidx, 15))
        );
    
        cjson_delete(goods);
        exit(EXIT_SUCCESS);
    }

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

    CC = gcc
    DEBUG = -ggdb3 -Wall -D_DEBUG
    DIR = -I.
    LIB = -lm
    RUN = $(CC) $(DEBUG) -o $@ $^ $(LIB)
    RUNO = $(CC) -c -o $@ $< $(DIR)
    TEST = -nostartfiles -e $(*F)
    RUNMAIN = $(RUN) $(TEST)
    
    all:test_readstr.out test_readfile.out
    
    # 库文件编译
    %.o:%.c
        $(RUNO)
    
    test_readstr.out:test_cjson.o scalloc.o tstr.o schead.o scjson.o
        $(RUNMAIN)
    
    test_readfile.out:test_cjson.o scalloc.o tstr.o schead.o scjson.o
        $(RUNMAIN)
    
    # 清除命令
    .PHONY:clean
    clean:
        rm -rf *.i *.s *.o *.out *~ ; ls

    编译结果展示 

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

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

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

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

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

    _Success_(return != EOF)
    _Check_return_opt_
    _ACRTIMP int __cdecl fgetc(
        _Inout_ FILE* _Stream
        );

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

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

    后记 - OK

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

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

  • 相关阅读:
    Js操作Select大全
    Mybatis 中获得 connection
    A②权限管理角色列表——ui效果;表格展开列;添加编辑删除自己写注意点;id的传参
    A③权限管理表格展开得权限思路;删除标签按钮实现思路
    报错记录:vue.runtime.esm.js?2b0e:619 [Vue warn]: Invalid prop: type check failed for prop "data". Expected Array, got Object
    表单校验小tip
    A①权限管理权限列表——git创新分支;请求数据带有参数type;权限等级tag标签;权限管理思路
    网页导航菜单弹出子菜单的制作
    细线表格的制作方法
    下拉的DIV+CSS+JS二级树型菜单,刷新无影响
  • 原文地址:https://www.cnblogs.com/life2refuel/p/5792581.html
Copyright © 2011-2022 走看看