【开源共享】拿来即用的C语言字符串函数库

tech2026-04-07  1

在线课堂:https://www.100ask.net/index(课程观看) 论  坛:http://bbs.100ask.net/(学术答疑) 开 发 板:https://100ask.taobao.com/ (淘宝)      https://weidongshan.tmall.com/(天猫) 交流群一:QQ群:869222007(鸿蒙开发/Linux/嵌入式/驱动/资料下载) 交流群二:QQ群:536785813(单片机-嵌入式) 公 众 号:百问科技


版本日期作者说明V12020嵌入式Hacker技术文档

一、字符串函数库:Simple Dynamic Strings 1.简介 Simple Dynamic Strings (简称 SDS) 是一个 C 语言字符串库,它增强了 C 语言字符串处理的能力。 设计 SDS 原本是为了满足设计者自身日常的 C 编程,后来又被转移到 Redis 中,在 Redis 中被广泛使用并对其进行了修改以适合于高性能操作。现在,它又被从 Redis 中提取出来的,并 fork 为一个独立项目。 只有 1500 行不到的代码,就能做到 3.2K 个 star,牛牛牛~~~ 它有什么优点? • 使用更简单; • 二进制安全; • 效率更高; • 与 C 字符串函数兼容;

源码链接:

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 内部有数据结构保存着字符串的长度; • 长度与字符串内是否有 NULL 无关;

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[]; };

• len 存储的是字符串长度; • 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 章节


百问网技术论坛: http://bbs.100ask.net/

百问网官方wiki(资料下载): http://wiki.100ask.org/

线上课堂: https://www.100ask.net/index

百问网开发板: 淘宝:https://100ask.taobao.com/ 天猫:https://weidongshan.tmall.com/

技术交流群(鸿蒙开发/Linux/嵌入式/驱动/资料下载) QQ群:869222007

单片机-嵌入式Linux交流群: QQ群:536785813

百问科技公众号

百问科技服务号
最新回复(0)