c json實戰引擎四 , 最後❤跳躍

来源:http://www.cnblogs.com/life2refuel/archive/2016/08/21/5792581.html
-Advertisement-
Play Games

scjson純c json引擎, 補充了可以在json串或文件中添加註釋的功能. window和linux測試完畢. ...


引言  - 以前那些系列

  長活短說, 寫的最終 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 != '\n')
                    ;
                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. // 註釋直接 捨棄到 \n, 行尾

  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 != '\n')
                    ;
                continue;
            }

            // step 4 : 處理 /*
            if (to[1] == '*') {
                while ((c = *++to) && (c != '*' || to[1] != '/'))
                    ;
                if (c)
                    to += 2;
                continue;
            }
        }

        // step 5 : 合法數據直接保存
        *in++ = *to++;
    }

    *in = '\0';
    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>='\t'&&c<='\r'))

// 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 "\n",\
         __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 獲取數組長度,只能是數組類型或""字元串常量,後者包含'\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('\n' != getchar()) \
            ;\
        puts(_STR_SAFETY_SCANF);\
    }\
    while('\n' != getchar())
#endif /*!SAFETY_SCANF*/

// 簡單的time幫助巨集
#ifndef TIME_PRINT
#define _STR_TIME_PRINT "The current code block running time:%lf seconds\n"
#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__)
/*
 * 返回申請的記憶體, 並且是填充'\0'
 * sz    : 申請記憶體的長度
 */
#define sm_malloc(sz)        sm_malloc_(sz, __FILE__, __LINE__, __func__)
/*
 * 返回申請到num*sz長度記憶體, 並且是填充'\0'
 * 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, 返回記憶體會用'\0'初始化
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!\n", 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<\n", 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) {
    
              
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在php中,結果輸出一共有兩種方式:echo和print,下麵將對兩種方式做一個比較。 echo與print的區別: echo print 連續輸出字元串 能連續輸出多個字元串 只能輸出一個字元串 返回值 無 返回1 用法 echo或echo() print或print() (1)echo能連續輸出... ...
  • python 操作郵件,不是很方便,說實話還不是理解的特別透徹,這次想把自己碰到的東西總結下來 郵件有imap,pop,imap協議,這次使用的是imap4協議,主要用了imap4和mail類, 代碼主要參考了http://blog.csdn.net/bonnshore/article/detail ...
  • Esc鍵編輯器(從工具視窗) F1 幫助 千萬別按,很卡! F2(Shift+F2) 下/上高亮錯誤或警告快速定位 F3 向下查找關鍵字出現位置 F4 查找變數來源 F5 複製文件/文件夾 F6 移動 F11 切換書簽 F12 返回到以前的工具視窗 註意:部分快捷鍵,必須在沒有更改快捷鍵的情況下才可 ...
  • 開發Java程式之前,需要在電腦行安裝並配置Java開發環境。一種是直接安裝Myeclipse,利用其自帶的JDK編譯運行;另一種是在我們的Windows或者Linux平臺下安裝JDK,配置環境變數。(隨著JDK版本的更新,Myeclipse預設JDK是不變的,除非升級最新版的Myeclipse) ...
  • CvSize 矩形框大小,以像素為精度 GetSize返回矩陣或圖像ROI的大小 CvSize cvGetSize( const CvArr* arr );arr 數組頭。 函數 cvGetSize 返回圖像或矩陣的行數和列數,如果是圖像就返回ROI的大小 註意: IplImage* src1=cv ...
  • 一. 準備工作 1. 點擊此下載相關開發工具 2. 將poi-3.8、jxls-core-1.0兩個jar包放到工程中,並引用 3. 將excel模板runRecord.xls放到RunRecordBSImpl.java類路徑下 二. RunRecordBSImpl.java類 1 import j ...
  • 一、概述 為了讓web伺服器和web應用程式進行訪問交互,servlet是這個交互的標準介面,web伺服器必須符合servlet標準,web應用應該實現servlet介面。 tomcat是一個符合servlet標準的servlet容器。如下圖所示。 二、tomcat作為servlet容器的基本功能 ...
  • 1.什麼是php PHP,即“Hypertext Preprocessor”,是一種被廣泛應用的開源通用腳本語言,尤其適用於 Web 開發並可嵌入 HTML 中去。它的語法利用了 C、Java 和 Perl,易於學習。該語言的主要目標是允許 web 開發人員快速編寫動態生成的 web 頁面。對於像微 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...