欢迎大家来到IT世界,在知识的湖畔探索吧!
作者:嵌入式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
欢迎大家来到IT世界,在知识的湖畔探索吧!
源码文件:
欢迎大家来到IT世界,在知识的湖畔探索吧!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():
欢迎大家来到IT世界,在知识的湖畔探索吧!#include <stdio.h>
#include "sds.h"
#include "sdsalloc.h"
int main(void)
{
sds mystr = sdsnew("Hello World!");
printf("%s\n", mystr);
sdsfree(mystr);
}
运行效果:
nbsp;gcc -o sdsdemo sds.c sdsdemo.c
nbsp;./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));
}
运行效果:
nbsp;./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);
}
运行效果:
nbsp;./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);
}
运行效果:
nbsp;./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);
}
运行效果:
nbsp;./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);
}
运行效果:
nbsp;./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);
}
运行效果:
nbsp;./sdsdemo
-my string-
去掉了空格和换行符。
sdsrange():截取指定范围内的字符串
int main(void)
{
sds s = sdsnew("Hello World!");
sdsrange(s,1,4);
printf("-%s-\n", s);
}
运行效果:
nbsp;./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 个参数指定分割符为空格。
运行效果:
nbsp;./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);
}
运行效果:
nbsp;./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 章节
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/35083.html