函数位于glibc源码中的../glibc-version/string/argz-ctsep.c中,其作用是将字符串以指定的字符进行分割,指定字符的位置替换成空字符串(\0),使整个字符串形成一个argz vector。
argz vector是存储在连续空间的一维字符数组,彼此之间以空字符(\0)进行分隔。
也就是说argz_create_sep函数的目的是将一个字符串按指定字符分割成多个字符串,存入一个连续空间中,彼此之间以空字符(\0)进行分隔,形成argz vector。
假如待分割字符串为“argz_create_sep”,以r作分割符,得到的argz vector应当如下所示:
函数声明如下:
// argz.h
/* Make a '\0' separated arg vector from a SEP separated list in
STRING, returning it in ARGZ, and the total length in LEN. If a
memory allocation error occurs, ENOMEM is returned, otherwise 0.
The result can be destroyed using free. */
extern error_t __argz_create_sep (const char *__restrict __string,
int __sep, char **__restrict __argz,
size_t *__restrict __len) __THROW;
extern error_t argz_create_sep (const char *__restrict __string,
int __sep, char **__restrict __argz,
size_t *__restrict __len) __THROW;
/* */
函数的实现如下:
error_t
__argz_create_sep (const char *string, int delim, char **argz, size_t *len)
{
size_t nlen = strlen (string) + 1;
if (nlen > 1)
{
const char *rp;
char *wp;
*argz = (char *) malloc (nlen);
if (*argz == NULL)
return ENOMEM;
rp = string;
wp = *argz;
do
if (*rp == delim)
{
if (wp > *argz && wp[-1] != '\0')
*wp++ = '\0';
else
--nlen;
}
else
*wp++ = *rp;
while (*rp++ != '\0');
if (nlen == 0)
{
free (*argz);
*argz = NULL;
*len = 0;
}
*len = nlen;
}
else
{
*argz = NULL;
*len = 0;
}
return 0;
}
weak_alias (__argz_create_sep, argz_create_sep)
/* */
代码相对比较简单,逐字符进行复制,遇到与delim相同的就替换成\0,然后继续复制。
需要注意的是如果待分割的字符串中存在连续的delim,那么形成的argz vector中该部分字符串只有一个\0,同时len也会相应的减小。完成这个操作的是下面这段代码,它主要是针对这类特殊情况的处理(假设以s进行分割)。
1、待分割字符串string的第一个字符就是delim相同(如:string Microsoft)
2、待分割字符串string中存在连续个delim(如:string Microsssoft,或者ssssssssss)
if (wp > *argz && wp[-1] != '\0')
*wp++ = '\0';
else
--nlen;
/* */
调用__argz_create_sep函数的方式为:
const char *mystring = "string microsoft";
char delim = 's';
__argz_create_sep(mystring, delim, &argz, &len);
/* */
写个程序测试一下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ENOMEM 12
#define MYLEN 4
#ifndef __error_t_defined
typedef int error_t;
#endif
error_t __argz_create_sep (const char *string, int delim, char **argz, size_t *len)
{
size_t nlen = strlen(string) + 1;
if (nlen > 1)
{
const char *rp;
char *wp;
*argz = (char *) malloc (nlen);
if (*argz == NULL)
return ENOMEM;
rp = string;
wp = *argz;
do
if (*rp == delim)
{
if (wp > *argz && wp[-1] != '\0')
*wp++ = '\0';
else
--nlen;
}
else
*wp++ = *rp;
while (*rp++ != '\0');
if (nlen == 0)
{
free (*argz);
*argz = NULL;
*len = 0;
}
*len = nlen;
}
else
{
*argz = NULL;
*len = 0;
}
return 0;
}
int main(void)
{
int i, j;
size_t len[MYLEN];
char *argz[MYLEN];
char *testString[MYLEN] = {
"function converts the argz",
"tfunction converts the argz",
"tttttttt",
""
};
/* 以t作分割符 */
for(i=0; i<MYLEN; ++i){
__argz_create_sep(testString[i], 't', &argz[i], &len[i]);
}
for(i=0; i<MYLEN; ++i)
{
printf("NO.%d, lenght: %d\n+", i, len[i]);
for(j=0; j<len[i]; ++j){
putchar('-');
}
printf("+\n|");
for(j=0; j<len[i]; ++j){
putchar(argz[i][j]);
}
printf("|\n+");
for(j=0; j<len[i]; ++j){
putchar('-');
}
printf("+\n\n");
}
for(i=0; i<MYLEN; ++i){
if(argz[i]){
free(argz[i]); argz[i]=NULL;
}
}
return 0;
}
程序输出如下:
注意,输出结果中argz vector的字符串之间的“空格”并不是空格,而是\0的输出但是这个字符没法显示的结果。
附:我觉得那个函数写的太蛋疼了。。果然我还是喜欢用for啊。。。
error_t __argz_create_sep (const char *string, int delim, char **argz, size_t *len)
{
size_t nlen = strlen (string) + 1;
const char *rp;
char *wp;
if(nlen <= 1)
{
*argz = NULL;
*len = 0;
return 0;
}
*argz = (char *) malloc (nlen);
if (*argz == NULL) return ENOMEM;
for(rp = string, wp = *argz; *rp; ++rp)
{
if(*rp == delim)
{
if(wp > *argz && wp[-1] != '\0'){
*wp++ = '\0';
}
else{
--nlen;
}
}
else{
*wp++ = *rp;
}
}
if (nlen == 0)
{
free (*argz);
*argz = NULL;
}
*len = nlen;
return 0;
}
现在是2017年4月14日,看到以前写的这一段内容,仔细思考了一下,果然还是源码厉害。for并不能完全的处理由delim组成的字符串。如string="ssss", delim='s',当处理到最后一个字符时,源码很可靠的让nlen=1,并且argz[0]='\0'。但是我上面那个用for写的,则没有处理argz[0]='\0',直接导致argz中len=1,但是却什么都没有。