【開源共享】拿來即用的C語言字符串函數庫

2021-03-02 百問科技

一、字符串函數庫:Simple Dynamic Strings1. 簡介

Simple Dynamic Strings (簡稱 SDS) 是一個 C 語言字符串庫,它增強了 C 語言字符串處理的能力。

設計 SDS 原本是為了滿足設計者自身日常的 C 編程,後來又被轉移到 Redis 中,在 Redis 中被廣泛使用並對其進行了修改以適合於高性能操作。現在,它又被從 Redis 中提取出來的,並 fork 為一個獨立項目。

只有 1500 行不到的代碼,就能做到 3.2K 個 star,牛牛牛~~~

它有什麼優點?

源碼連結:

https://github.com/antirez/sds

源碼文件:

sds.c
sdsalloc.h
sds.h
testhelp.h

相關 API:

sds sdsnewlen(const void *init, size_t initlen) 
sds sdsempty(void) 
sds sdsnew(const char *init) 
sds sdsdup(const sds s) 
void sdsfree(sds s) 
void sdsupdatelen(sds s) 
void sdsclear(sds s) 
sds sdsMakeRoomFor(sds s, size_t addlen) 
sds sdsRemoveFreeSpace(sds s) 
size_t sdsAllocSize(sds s) 
void *sdsAllocPtr(sds s) 
void sdsIncrLen(sds s, ssize_t incr) 
sds sdsgrowzero(sds s, size_t len) 
sds sdscatlen(sds s, const void *t, size_t len) 
sds sdscat(sds s, const char *t) 
sds sdscatsds(sds s, const sds t) 
sds sdscpylen(sds s, const char *t, size_t len) 
sds sdscpy(sds s, const char *t) 
int sdsll2str(char *s, long long value) 
int sdsull2str(char *s, unsigned long long v) 
sds sdsfromlonglong(long long value) 
sds sdscatvprintf(sds s, const char *fmt, va_list ap) 
sds sdscatprintf(sds s, const char *fmt, ...) 
sds sdscatfmt(sds s, char const *fmt, ...) 
sds sdstrim(sds s, const char *cset) 
void sdsrange(sds s, ssize_t start, ssize_t end) 
void sdstolower(sds s) 
void sdstoupper(sds s) 
int sdscmp(const sds s1, const sds s2) 
sds *sdssplitlen(const char *s, ssize_t len, const char *sep, int seplen, int *count) 
void sdsfreesplitres(sds *tokens, int count) 
sds sdscatrepr(sds s, const char *p, size_t len) 
int is_hex_digit(char c) 
int hex_digit_to_int(char c) 
sds *sdssplitargs(const char *line, int *argc) 
sds sdsmapchars(sds s, const char *from, const char *to, size_t setlen) 
sds sdsjoin(char **argv, int argc, char *sep) 
sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen)

1. 比較常用的功能1.1 創建字符串

sdsnew() 和 sdsfree():

#include <stdio.h>
#include "sds.h"
#include "sdsalloc.h"

int main(void)
{
    sds mystr = sdsnew("Hello World!");
    printf("%s\n", mystr);
    sdsfree(mystr);
}

運行效果:

$ gcc -o sdsdemo sds.c sdsdemo.c
$ ./sdsdemo
Hello World!

看到了嗎?

printf 直接就可以列印 sds,這就是說 sds 本身就是 C 語言的字符串類型

sds 的定義如下:

typedef char *sds;

也就是說,sds 是能兼容 libc 裡字符串處理函數 (例如strcpy, strcat...)的。

當不再使用 sds 字符串時,就算是空串,也要通過 sdsfree 銷毀字符串。

1.2 獲取字符串長度

sdsnewlen():

int main(void)
{
    char buf[3];
    sds mystring;

    buf[0] = 'A';
    buf[1] = 'B';
    buf[2] = 'C';
    mystring = sdsnewlen(buf,3);
    printf("%s of len %d\n", mystring, (int) sdslen(mystring));
}

運行效果:

$ ./sdsdemo
ABC of len 3

和 strlen() 有 2 點不同

運行時長固定,sds 內部有數據結構保存著字符串的長度;

1.3 拼接字符串

sdscat():

int main(void)
{
    sds s = sdsempty();
    s = sdscat(s, "Hello ");
    s = sdscat(s, "World!");
    printf("%s\n", s);
}

運行效果:

$ ./sdsdemo
Hello World!

sdscat 接受的參數是以 NULL 結尾的字符串,如果想擺脫這個限制,可以用 sdscatsds()。

sdscatsds():

int main(void)
{
    sds s1 = sdsnew("aaa");
    sds s2 = sdsnew("bbb");
    s1 = sdscatsds(s1,s2);
    sdsfree(s2);
    printf("%s\n", s1);
}

運行效果:

$ ./sdsdemo
aaabbb

1.4 擴展字符串長度

sdsgrowzero():

int main(void)
{
    sds s = sdsnew("Hello");
    s = sdsgrowzero(s,6);
    s[5] = '!'; /* We are sure this is safe*/
    printf("%s\n", s);
}

運行效果:

$ ./sdsdemo
Hello!

1.5 格式化字符串

sdscatprintf():

int main(void)
{
    sds s;
    int a = 10, b = 20;
    s = sdsnew("The sum is: ");
    s = sdscatprintf(s,"%d+%d = %d",a,b,a+b);
    printf("%s\n", s);
}

運行效果:

$ ./sdsdemo
The sum is: 10+20 = 30

1.6 截取字符串

sdstrim():去掉指定字符

int main(void)
{
    sds s = sdsnew("         my string\n\n  ");
    sdstrim(s," \n");
    printf("-%s-\n",s);
}

運行效果:

$ ./sdsdemo
-my string-

去掉了空格和換行符。

sdsrange():截取指定範圍內的字符串

int main(void)
{
    sds s = sdsnew("Hello World!");
    sdsrange(s,1,4);
    printf("-%s-\n", s);
}

運行效果:

$ ./sdsdemo
-ello-

1.7 字符串分割 (Tokenization)

sdssplitlen() 和 sdsfreesplitres():

int main(void)
{
    sds *tokens;
    int count, j;

    sds line = sdsnew("Hello World!");
    tokens = sdssplitlen(line, sdslen(line)," ",1,&count);

    for (j = 0; j < count; j++)
        printf("%s\n", tokens[j]);
    sdsfreesplitres(tokens,count);
}

sdssplitlen() 第 3和4 個參數指定分割符為空格。

運行效果:

$ ./sdsdemo
Hello
World!

1.8 字符串合併 (String joining)

sdssplitlen() 和 sdsfreesplitres():

int main(void)
{
    char *tokens[3] = {"foo","bar","zap"};
    sds s = sdsjoin(tokens, 3, "|");
    printf("%s\n", s);
}

運行效果:

$ ./sdsdemo
foo|bar|zap

還有其他一些功能,用到再研究吧!

2. 簡單了解一下內部實現

在 SDSD 中,使用二進位前綴(頭部) 來保存字符串相關的信息,該頭部存儲在 SDS 返回給用戶的字符串的實際指針之前:

+---+-+-+
| Header | Binary safe C alike string... | Null term |
+---+-+-+
         |
         `-> Pointer returned to the user.

這個 Header 在代碼中用結構體來描述,該結構體定義大致如下:

struct sdshdr {
    [...]
    int len;
    char buf[];
};

假設你使用的字符串為 "HELLOWORLD",為了提升效率,SDS 可能會提前分配多一些空間,所以實際的內存布局如下:

+--+----+-+\
| len | buf | H E L L O W O R L D \n | Null term |  Free space   \
+--+----+-+\
             |
             `-> Pointer returned to the user.

現在,我們來看一下 SDS 分配字符串的大致步驟:

sds sdsnew(const char *init)
    initlen = (init == NULL) ? 0 : strlen(init);
    sdsnewlen(init, initlen);
        int hdrlen = sdsHdrSize(type);      // 確定 Header 的長度
        sh = s_malloc(hdrlen+initlen+1);    // 分配 Header + String + 1 個字節的空間

        s = (char*)sh+hdrlen;       // 保存 C string 的地址
        SDS_HDR_VAR(8,s);           // 定義 struct sdshdr sh
        sh->len = initlen;          // 初始化 struct sdshdr sh

        if (initlen && init)        // 初始化 C string 
            memcpy(s, init, initlen);
        
        s[initlen] = '\0';          // 總是添加一個 NULL
        return s;                   // 返回 C string

其他的 SDS API 是如何實現的,就留給大家自行分析了。

3. 相關參考

-《Linux程序設計》,6,7.1 章節

-《C primer plus》,11,12 章節

-《C 和指針》,9 章節

-《Linux 系統編程》,9 章節

-《C專家編程》,7.5 章節

-《C和C++程式設計師面試秘笈》,4 章節


相關焦點

  • C語言:優雅的字符串函數庫
    例如:用新語言寫代碼注釋 / commit message / README / issue;對了,我作為英文的愛好者,一直想重啟我的英文學習之路,後續想在公眾號裡記錄一些英文相關的知識,請你們不要笑話我~~~二、字符串函數庫:Simple
  • C語言字符數組和字符串
    字符串數據類型是建模在形式字符串的想法上的數據類型。字符串是幾乎在所有程式語言中可以實現的非常重要和有用的數據類型。在某些語言中它們可作為基本類型獲得,在另一些語言中做為複合類型獲得。多數高級語言的語法允許通常用某種方式引用起來的字符串來表示字符串數據類型的實例;這種元字符串叫做「文本」或「字符串文本」。
  • 學習c語言筆記——C庫函數printf()
    c語言中的printf是什麼來的?」。我答:「它是一個函數,主要用來輸出運算結果。」 ,下面就給大家介紹C庫函數printf()使用方法。下面我們通過一個調用c庫函數的c語言案例來說明printf()函數的使用方法,如c語言1。
  • C語言字符串處理函數之字符串轉換、查詢函數
    介紹完字符串整體操作函數,就該到字符串查詢函數和字符串轉換函數了,至於一些字符串轉換函數,如atoi(),atof(),strtod(),strtol(),tolower(),toupper()等,以後有時間再整理整理。
  • 在C語言中如何高效地複製和連接字符串?
    作者 | Martin Sebor譯者 | 蘇本如,責編 | 劉靜以下為譯文:在所有標準C語言<string.h>頭文件中聲明的字符串處理函數中,最常用的是那些用來複製和連接字符串的函數。兩個或多個字符串的連接操作的最佳複雜度和字符數量成線性關係。但是,如上所述,讓函數返回指向目標字符串的指針會導致操作的效率明顯低於最佳效率。該函數遍歷源字符串序列和目標字符串序列,並獲取指向這兩個序列末尾的指針。該指針指向函數(strncpy除外)附加到目標序列上的字符串結束符NUL('\0')處或它的後一位。
  • C語言常用的庫函數
  • C語言|字符串的複製、連接和比較及字符串長度
    字符串複製函數strcpy()strcpy(s1,s2);該函數把字符串s2複製到s1,直到遇到s2中的』\0』為止。s1要有足夠的空間容納s2,且s1中的內容被覆蓋,函數返回的是s1如果函數執行前s1有初值,那麼值被覆蓋由於函數返回值是s1,也可以直接輸出此函數2.
  • php字符串常用處理,運算符和幾個常用的字符串函數
    本篇將介紹php字符串常用處理,運算符和幾個常用的字符串函數。有興趣的朋友可以了解一下!php是一門很受歡迎的程式語言之一,由於它的語法簡單易學,迎來了一大批的自學者,小編就是其中一個。php確實相當於其它語言(java、c#等等)比較簡單,但是對一個毫無程式語言基礎的人來說,還是有一定難度的。
  • 通過這 9 本開源好書學習 C 語言
    – 聲明, 定義,typedef, const 和 volatile以及序列點庫 – 診斷, 字符處理, 本地化, 與具體實現相關的限制, 數學函數, 非局部跳轉, 信號處理, 可變參數, 輸入輸出, 格式化 I/O, 字符 I/O, 無格式 I/O, 隨機訪問函數, 通用函數, 字符串處理, 日期和時間C 語言編寫的完整程序 – 綜合以上內容, main函數的參數,
  • ​【轉載】字符轉數字:C語言裡atoi函數和它的童鞋
    在編程的過程中,我們可能因為某些功能需要,需要在字符和數字之間做互換,那麼如果你不知道有某些庫函數已經把這個小功能實現了,你又會繼續去「 造輪子 」!也就是根據自己對這塊內容的理解,自己去寫一個這樣的函數,其實這樣也挺好的,你的思維得到了鍛鍊。話不多說,乾貨如下!
  • C語言中常用的幾個頭文件及庫函數
    來源:https://www.jb51.net/article/124594.htm這篇文章主要介紹了C語言中常用的幾個頭文件及庫函數的相關資料
  • 【C語言更新】C語言中字符串的操作
    void  *memcpy(void *dest, const void *src, size_t n)void  *memmove(void *dest, const void *src, size_t n)另一個用於從 src 複製  n 個字符到 dest 的函數。
  • C/C++中字符串與數字轉換
    stingstream能將任何類型輕鬆轉變為字符串類型,也能將字符串類型轉變為數字類型。有點類似<stdio.h>中的sprintf和sscanf函數,但是stringstream操作更加的安全、不會產生數組溢出等問題,而且操作簡單。注意stringstream不會主動釋放內存,要使用clear()函數釋放內存。
  • php字符串函數
    addcslashes — 為字符串裡面的部分字符添加反斜線轉義字符addslashes — 用指定的方式對字符串裡面的字符進行轉義
  • C語言strcmp和strcpy的用法
    說明:strcmp()函數是根據ACSII碼的值來比較兩個字符串的;strcmp()函數首先將s1字符串的第一個字符值減去s2第一個字符,若差值為零則繼續比較下去;若差值不為零,則返回差值。一般形式:strcmp(字符串1,字符串2)說明:當s1<s2時,返回為負數當s1=s2時,返回值= 0當s1>s2時,返回正數即:兩個字符串自左向右逐個字符相比(按ASCII值大小相比較),直到出現不同的字符或遇'\0'為止。
  • (基礎篇)PHP字符串函數
    PHP字符串函數包括查找字符位置函數;提取子字符函數;替換字符串;字符長度;比較字符函數;分割成數組字符;去除空格等等
  • 一文帶你了解c++和c中字符串的使用
    有可能有些網友還沒怎麼接觸到c++(c++它是一門面向對象的語言,而c是一門面向過程的語言,所以這裡可能沒接觸過那個面向對象的網友不習慣這個用法,不過還是建議至少要掌握一門面向對象的語言,在這個發展快速的時代,不能太固步自封了(我這裡也是簡單的介紹一下c++中的字符串,不會設計到類和對象什麼的,只是和c語言做個對比)。)1、什麼是字符串?
  • C語言實現將字符串轉換為數字的方法
    這篇文章主要介紹了C語言實現將字符串轉換為數字的方法,涉及系統函數atoi()函數的使用技巧,需要的朋友可以參考下本文實例講述了C語言實現將字符串轉換為數字的方法。
  • useful R 字符串函數
    R語言中有很多非常有用的函數,如果評有沒有用實在是件比較艱難的事情,其實我們在之前也已經講了很多有用的函數,但是都是比較簡單的函數(簡單但是常用
  • c語言指針與字符數組
    我們所說的字符串就是字符數組的一種,就如我們常見的"hello world!",字符串的使用在C語言中也是非常重要的,常常會遇到一些操作,如字符串的修改、拷貝、字符串長度等,在物聯網的應用中也尤為突出,物聯網應用中所用的模組,大多是需要使用AT指令的,這就需要對字符串的操作。