你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

C语言——字符串函数库

[复制链接]
gaosmile 发布时间:2020-8-31 23:09
一、沉浸式学习

以学习一门语言为例:
大多数人都持有一种观念,要真正学好一门语言必须得去所学语言当地学习或生活一段时间。

而事实上,大多数人都没有这样的学习条件。

解决问题的方法是:
自行改造环境,为自己创造沉浸式的学习环境。

例如:

  • 看新语言的电影;

  • 更改手机、电脑的语言设置;

  • 看新语言的文档和书籍;

  • 用新语言写 todo list;

  • 用新语言来学习自己需要的专业知识;

  • 翻译新语言的文档并分享;

  • 逛所学语言的论坛和网站;

  • 用新语言写代码注释 / commit message / README / issue;

  • ...


对了,我作为英文的爱好者,一直想重启我的英文学习之路,后续想在公众号里记录一些英文相关的知识,请你们不要笑话我~~~


二、字符串函数库:Simple Dynamic Strings1. 简介

Simple Dynamic Strings (简称 SDS) 是一个 C 语言字符串库,它增强了 C 语言字符串处理的能力。

设计 SDS 原本是为了满足设计者自身日常的 C 编程,后来又被转移到 Redis 中,在 Redis 中被广泛使用并对其进行了修改以适合于高性能操作。现在,它又被从 Redis 中提取出来的,并 fork 为一个独立项目。

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

它有什么优点?

  • 使用更简单;

  • 二进制安全;

  • 效率更高;

  • 与 C 字符串函数兼容;


源码链接:

http://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)
2. 比较常用的功能2.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 销毁字符串。

2.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 无关;


2.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
2.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!
2.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
2.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-
2.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!
2.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

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

3. 简单了解一下内部实现

在 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 是如何实现的,就留给大家自行分析了。

4. 相关参考

-《Linux程序设计》,6,7.1 章节

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

-《C 和指针》,9 章节

-《Linux 系统编程》,9 章节

-《C专家编程》,7.5 章节

-《C和C++程序员面试秘笈》,4 章节


收藏 评论0 发布时间:2020-8-31 23:09

举报

0个回答

所属标签

STM32团队

意法半导体微控制器和微处理器拥有广泛的产品线,包含低成本的8位单片机和基于ARM® Cortex®-M0、M0+、M3、M4、M33、M7及A7内核并具备丰富外设选择的32位微控制器及微处理器


最新内容

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
微信公众号二维码 微信公众号
手机版二维码 手机版