*s, int len, const char *sep, int seplen, int *count);void sdsfreesplitres(sds *tokens, int count);
和往常一样,这个函数可以处理SDS字符串和普通的C字符串。头两个参数s和len指定了要单元化的字符串,另两个字符串sep和seplen是在单元化过程中用到的分割符。最后的参数count是一个整数指针,会被设为返回的单元(子字符串)的数目。
返回值是一个在堆上分配的SDS字符串数组。
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);
output> Hellooutput> World!
返回的数组是在堆上分配的,并且数组的单个元素是普通的SDS字符串。在例子中,你可以通过调用sdsfreesplitres()释放所有资源。你也可以选择用free函数自行释放数组,或者像通常那样释放单独的SDS字符串。
合理的方法是用某种方式将你会重用的数组元素设置为NULL,并且用sdsfreesplitres()来释放其余所有的数组。
面向命令行的单元化
用分割符分割字符串是很有用的操作,但是对于执行最常见的涉及到重要的字符串操作,即为程序实现命令行接口来说,通常还是不够的。
这是为什么SDS也提供一个额外的函数,允许你将用户由键盘交互式输入,或者通过一个文件、网络或者其他任何方式的参数,分割成单元。
sds *sdssplitargs(const char *line, int *argc);
sdssplitargs函数返回一个SDS字符串数组,就像sdssplitlen()一样。释放结构的函数sdsfreesplitres(),也是一样的。不同在于执行单元化的方式。
例如,如果输入下面一行:
call "Sabrina" and "Mark Smith\n"
函数会返回下面的标记(token):
“call”
“Sabrina”
“and”
“Mark Smith\n”
基本上,不同的标记要被一个或多个空格分割,每一个标记也可以是一个sdscatrepr()可以发出的相同格式的引用字符串。
字符串结合(Joining)
有两个函数做与单元化相反的工作,将字符串结合成一个。
sds sdsjoin(char **argv, int argc, char *sep, size_t seplen);sds sdsjoinsds(sds *argv, int argc, const char *sep, size_t seplen);
这两个函数取一个长度为argc的字符串数组,一个分割符及其长度作为输入,产生一个由所有被输入分割符分割的输入字符串所组成的SDS字符串。
sdsjoin()和sdsjoinsds()不同点在于前者接收C空字符终结的字符串作为输入,而后者要求所有数组里的字符串须为SDS字符串。但是也因为这个原因,只有sdsjoinsds()能够处理二进制数据
char *tokens[3] = {"foo","bar","zap"};sds s = sdsjoin(tokens,3,"|",1);printf("%s\n", s);
output> foo|bar|zap
所有返回SDS指针的SDS函数,在内存不足的情况下,也有可能返回NULL,基本上这是唯一需要你进行检查的地方。
但是许多现代的C程序处理内存不足时,只会简单地中止程序,所以可能你也会需要通过包装malloc,直接调用其他相关的内存分配函数来处理这种情况。
在本篇开始时,解释了SDS字符串是如何被分配的,但是只涉及到保存在返回用户的指针之前的前缀,被当作一个字符串头(header)而已,没有更深入的细节。为了了解进阶的用法,最好挖掘更多SDS的本质,看看实现它所用到的结构体:
struct sdshdr {
int len;
int free;
char buf[];};
如你所见,这个结构体可能与某个传统的字符串库类似,但是结构体的buf域是不同的,因为它不是一个指针,而是一个没有声明任何长度的数组,所以buf实际上指向了紧跟叫free的整数后的第一个字节。所以为了创建一个SDS字符串,我们只要分配一片内存,其大小为sdshdr结构体加上我们的字符串长度,外加一个额外的字节,这是为了所有SDS字符串硬性需要的空字符。
结构体的len域显而易见,就是当前的SDS字符串的长度,每当字符串被通过SDS函数调用修改时,总是会被重新计算。而free域表示了在当前分配空间中的空闲内存的数量,可以被用来存储更多的字符。
所以实际的SDS内存分布是这个:
+------------+------------------------+-----------+---------------\
| Len | Free | H E L L O W O R L D \n | Null term | Free space \
+------------+------------------------+-----------+---------------\
|
Pointer returned to the user.
你可能要问,为什么在字符串末尾会有一些空闲空间,这看上去是浪费。实际上,在一个新的SDS字符串创建后,之后是没有任何空闲空间的:分配空间小到只需要保存字符串头、字符串和空终结符。然而,其他的访问模式会在末尾创建一些额外的空闲空间,如下面的程序:
s = sdsempty();s = sdscat(s,"foo");s = sdscat(s,"bar");s = sdscat(s,"123");
因为SDS致力于高效,它负担不起在每次添加新数据时,重新分配字符串,因为这会非常的低效,所以会使用每次你扩大字符串时,预分配一些空闲空间。
所使用的预分配算法如下:每次字符串为了保存更多的字节而被重新分配时,实际进行分配的大小是最小需求的两倍。例如,如果字符串现在保存了30个字节,我们多连接2个字节,SDS总共会分配64个字节,而非32个。
然而,可进行分配的空间有一个硬性限制,被定义为SDS_MAX_PREALLOC。SDS绝不会分配超过1MB的额外空间(默认的,你可以修改这个默认值)。
sds sdsRemoveFreeSpace(sds s);size_t sdsAllocSize(sds s);
有时,有一类程序要求使用非常少的内存。字符串连接、裁剪、取