字符串处理函数

一:查找

1:strcspn函数

         该函数是标准库的函数,包含在头文件<string.h>中,其原型如下:

size_t strcspn(const char *s1, const char *s2);

         该函数计算字符串s1中,从头开始的某子串的长度,该子串中的字符都不会在s2中出现。举例如下:

int main(int argc, char **argv)
{
    char *s1 = argv[1];
    char *s2 = argv[2];

    printf("the length is %d\n", strcspn(s1, s2));
}

$./1  abcdefg  hij

the length is 7

 

$./1  defgabc  hijad

the length is 0

 

$./1  defgabc  heija

the length is 1

 

2:strspn函数

         由strcspn函数引申,strspn的意义与其相反。它也是标准库函数,在头文件<string.h>中定义,其原型如下:

       size_t strspn(const char *s1, const char *s2);

        该函数计算字符串s1中,从头开始的某子串的长度,该子串中的字符都在s2中出现。举例如下:

int main(int argc, char **argv)
{
    char *s1 = argv[1];
    char *s2 = argv[2];

    printf("the length is %d\n", strspn(s1, s2));
}

$./1  abcde  fgh

the length is 0

 

$./1  abcde  fghaxzbhc

the length is 3

 

$./1  abcde  edcbaheh

the length is 5

 

3:index函数

       #include <strings.h>
       char * index(const char *string, int c);

        该函数等价于strchr函数,返回一个指针,该指针指向字符串string中,字符c第一次出现的位置,如果字符c未出现在string中,则返回NULL,举例如下:

int main(int argc, char **argv)
{
    char *s1 = argv[1];
    int c = argv[2][0];

    char *res1 = index(s1, c);
    char *res2 = strchr(s1, c);

    printf("res1 is %p\n", res1);
    printf("res2 is %p\n", res2);
}

$./1  abcdef  h

res1 is 0x0

res2 is 0x0

 

$./1 abcdef  a

res1 is 0x612f2d44

res2 is 0x612f2d44

 

$./1 abcdef c

res1 is 0x612f2d46

res2 is 0x612f2d46

 

4:strpbrk函数

         strpbrk是标准库函数,在头文件<string.h>中定义,其原型如下:

       char *strpbrk(const char *s, const char *accept);

        该函数在字符串s中,寻找accept字符串中任意字符的首次出现的位置。如果找到了任意字符,则返回指向该字符的指针,如果没找到,则返回NULL。

        该函数是线程安全的。例子如下:

int main(int argc, char **argv)
{
    if(argc != 3)
    {
        printf("argument error\n");
        return -1;
    }

    char *str = argv[1];
    char *accept = argv[2];

    char *res = strpbrk(str, accept);
    if(res == NULL)
    {
        printf("NULL\n");
    }
    else
    {
        printf("res is %s\n", res);
    }
}
$ ./1 "//abc/def/hehe/abc:/abc:def//"  ""
NULL


$./1  "//abc/def/hehe/abc:/abc:def//"  ":/"

res is //abc/def/hehe/abc:/abc:def//

 

$./1 "//abc/def/hehe/abc:/abc:def//"   ":"

res is :/abc:def//

 

$./1 "//abc/def/hehe/abc:/abc:def//"   "a"

res is abc/def/hehe/abc:/abc:def//

 

$./1 "//abc/def/hehe/abc:/abc:def//"   "h"

res is hehe/abc:/abc:def//

 

$./1 "//abc/def/hehe/abc:/abc:def//"   "z"

NULL

 

二:分割

1:strtok和strtok_r函数

         strtok函数是标准库函数,包含在头文件 <string.h>中,其原型如下:

       char *strtok(char *str, const char *delim);

         strtok函数用来将一字符串分割为一系列的子串。要得到原字符串被分割后的所有子串,需要多次调用strtok,每次调用都返回一个子串的首地址。第一次调用strtok时,str需要指向分割的字符串,但在后续的调用中,置str为NULL即可。

         delim参数指向的字符串包含了若干分割字符。在分割同一个字符串时,可以在delim中指定不同的分隔字符。

         strtok返回一个指向子串的指针,该子串以’\0’结尾。该子串不会包含分隔符。如果已经没有子串了,则该函数返回NULL。

 

         在strtok函数的实现中,内部使用一静态指针old,该指针保存的是每次搜索子串的起始地址。第一次调用strtok时,该指针就是原字符串的首字符。

        本次调用strtok要寻找的子串首地址,由str中的下一个非分隔字符决定。如果找到了这样的字符,则它就是本次子串的首字符。如果没有找到这样的子串,则strtok返回NULL。

        子串的结尾,由下一个分隔字符或者’\0’决定。如果找到了一个分隔字符,则该位置被置为’\0’,并且将old指向下一个字符。

         根据上面的描述,可知,两个或多个连续的分割字符会被当做一个分隔符,并且会忽略原字符串中起始字符或者结尾字符是分隔字符的情况。因此strtok返回的子串一定不会是空串。在最新的GLIBC2.22版本中,strtok的源码如下:

static char *olds;

char *strtok (char *s, const char *delim)
{
    char *token;
    if (s == NULL)
        s = olds;

    /* Scan leading delimiters.  */
    s += strspn (s, delim);
    if (*s == '\0')
    {
      olds = s;
      return NULL;
    }

    /* Find the end of the token.  */
    token = s;
    s = strpbrk (token, delim);
    if (s == NULL)
        /* This token finishes the string.  */
        olds = __rawmemchr (token, '\0');
    else
    {
        /* Terminate the token and make OLDS point past it.  */
        *s = '\0';
        olds = s + 1;
    }
    return token;
}


         因strtok函数内部使用了静态指针,因此它不是线程安全的。相应的线程安全函数是strtok_r,它是strtok函数的可重入版本。它的原型如下:

       char *strtok_r(char *str, const char *delim, char **saveptr);

         其中的saveptr参数,便充当了strtok中old指针的角色,strtok_r的代码与strtok基本一样,只不过其中的old都替换成了*saveptr。

 

        注意:这些函数会改变原字符串的内容,所以,第一个参数不能是只读的。例子如下:

int main(int argc, char **argv)
{
    if(argc != 3)
    {
        printf("argument error\n");
        return -1;
    }
    
    char *delim = argv[2];

    int strsize = strlen(argv[1])+1;
    char *str = calloc(strsize, sizeof(char));
    memcpy(str, argv[1], strsize-1);
    char *token = NULL;
    char *tmpstr = str;

    while((token = strtok(tmpstr, delim)) != NULL)
    {
        printf("\t%s\n", token);
        tmpstr = NULL;
    }

    int i = 0;
    for(i = 0; i < strsize; i++)
    {
        printf("%c ", str[i]);
    }
    printf("\n");
}

$ ./1 "//abc/def/hehe/abc:/abc:def//" ""
        //abc/def/hehe/abc:/abc:def//
/ / a b c / d e f / h e h e / a b c : / a b c : d e f / /

$ ./1  "//abc/def/hehe/abc:/abc:def//" ":/"
        abc
        def
        hehe
        abc
        abc
        def
/ / a b c  d e f  h e h e  a b c  / a b c  d e f  /


 

2:strsep函数

         strsep函数包含在头文件<string.h>中,其原型如下:

char *strsep(char **stringp, const char *delim);

         strsep函数与strtok函数的功能一样,也是用来分割字符串的。但是它们又有几处明显的不同:

         a:如果*stringp为NULL,则该函数不进行任何操作,直接返回NULL;

         b:strsep每次都是从*stringp指向的位置开始搜索,搜索到任一分割字符之后,将其置为’\0’,并使*stringp指向它的下一个字符。如果找不到任何分割字符,则将*stringp置为NULL。

         c:strsep内部没有使用静态指针,因而strsep是线程安全的。

         d:strsep返回的子串有可能是空字符串,实际上,就是因为strtok无法返回空子串,才引入的strsep函数。不过strtok符合C89/C99标准,因而移植性更好。但strsep却不是。

 

        在最新的GLIBC2.22版本中,strsep的源码如下:

 

char *strsep (char **stringp, const char *delim)
{
    char *begin, *end;

    begin = *stringp;
    if (begin == NULL)
        return NULL;

    /* A frequent case is when the delimiter string contains only one
     character.  Here we don't need to call the expensive `strpbrk'
     function and instead work using `strchr'.  */
    if (delim[0] == '\0' || delim[1] == '\0')
    {
        char ch = delim[0];

        if (ch == '\0')
            end = NULL;
        else
        {
            if (*begin == ch)
                end = begin;
            else if (*begin == '\0')
                end = NULL;
            else
                end = strchr (begin + 1, ch);
        }
    }
    else
        /* Find the end of the token.  */
        end = strpbrk (begin, delim);

    if (end)
    {
        /* Terminate the token and set *STRINGP past NUL character.  */
        *end++ = '\0';
        *stringp = end;
    }
    else
        /* No more delimiters; this is the last token.  */
        *stringp = NULL;

    return begin;
}

例子如下:

int main(int argc, char **argv)
{
    if(argc != 3)
    {
        printf("argument error\n");
        return -1;
    }
    
    char *delim = argv[2];

    int strsize = strlen(argv[1])+1;
    char *str = calloc(strsize, sizeof(char));
    memcpy(str, argv[1], strsize-1);
    char *token = NULL;
    char *tmpstr = str;

    while((token = strsep(&tmpstr, delim)) != NULL)
    {
        printf("\t%s\n", token);
    }

    int i = 0;
    for(i = 0; i < strsize; i++)
    {
        printf("%c ", str[i]);
    }
    printf("\n");
}


$ ./1 "//abc/def/hehe/abc:/abc:def//" ""
token is:  //abc/def/hehe/abc:/abc:def//
/ / a b c / d e f / h e h e / a b c : / a b c : d e f / /

$ ./1 "//abc/def/hehe/abc:/abc:def//" "/"
token is:
token is:
token is:  abc
token is:  def
token is:  hehe
token is:  abc:
token is:  abc:def
token is:
token is:
  a b c  d e f  h e h e  a b c :  a b c : d e f

$ ./1 "//abc/def/hehe/abc:/abc:def//" ":/"
token is:
token is:
token is:  abc
token is:  def
token is:  hehe
token is:  abc
token is:
token is:  abc
token is:  def
token is:
token is:
  a b c  d e f  h e h e  a b c   a b c  d e f


posted @ 2015-09-12 21:57  gqtc  阅读(327)  评论(0编辑  收藏  举报