zoukankan      html  css  js  c++  java
  • C语言常用的字符串操作函数

    1、初始化字符串

    1. #include <string.h>   
    2. void *memset(void *s, int c, size_t n);   
    3. //返回值:s指向哪,返回的指针就指向哪   

    memset函数把s所指的内存地址开始的n个字节都填充为c的值。通常c的值为0,把一块内存区清零。例如定义char buf[10];,如果它是全局变量或静态变量,则自动初始化为0(位于.bss段),如果它是函数的局部变量,则初值不确定,可以用memset(buf, 0, 10)清零,由malloc分配的内存初值也是不确定的,也可以用memset清零。

    2、取字符串的长度

    1. #include <string.h>   
    2. size_t strlen(const char *s);   
    3. //返回值:字符串的长度  

    strlen函数返回s所指的字符串的长度。该函数从s所指的第一个字符开始找'\0'字符,一旦找到就返回,返回的长度不包括'\0'字符在内。例如定义char buf[] = "hello";,则strlen(buf)的值是5,但要注意,如果定义char buf[5] = "hello";,则调用strlen(buf)是危险的,会造成数组访问越界。

    3、拷贝字符串

    1. #include <string.h>   
    2. void *memcpy(void *dest, const void *src, size_t n);   
    3. void *memmove(void *dest, const void *src, size_t n);   
    4. //返回值:dest指向哪,返回的指针就指向哪  

    memcpy函数从src所指的内存地址拷贝n个字节到dest所指的内存地址,和strncpy不同,memcpy并不是遇到'\0'就结束,而是一定会拷贝完n个字节。这里的命名规律是,以str开头的函数处理以'\0'结尾的字符串,而以mem开头的函数则不关心'\0'字符,或者说这些函数并不把参数当字符串看待,因此参数的指针类型是void *而非char *。

    memmove也是从src所指的内存地址拷贝n个字节到dest所指的内存地址,虽然叫move但其实也是拷贝而非移动。但是和memcpy有一点不同,memcpy的两个参数src和dest所指的内存区间如果重叠则无法保证正确拷贝,而memmove却可以正确拷贝。假设定义了一个数组char buf[20] = "hello world\n";,如果想把其中的字符串往后移动一个字节(变成"hhello world\n"),调用memcpy(buf + 1, buf, 13)是无法保证正确拷贝的:

    例 3.1错误的memcpy调用

    1. #include <stdio.h>   
    2. #include <string.h>   
    3.   
    4. int main(void)   
    5. {   
    6.     char buf[20] = "hello world\n";   
    7.     memcpy(buf + 1, buf, 13);   
    8.     printf(buf);   
    9.   
    10.     return 0;   
    11. }

    在我的机器上运行的结果是hhhllooworrd。如果把代码中的memcpy改成memmove则可以保证正确拷贝。memmove可以这样实现:

    1. void *memmove(void *dest, const void *src, size_t n)   
    2. {   
    3.     char temp[n];   
    4.     int i;   
    5.     char *d = dest;   
    6.     const char *s = src;   
    7.   
    8.     for (i = 0; i < n; i++)   
    9.         temp[i] = s[i];   
    10.     for (i = 0; i < n; i++)   
    11.         d[i] = temp[i];   
    12.   
    13.     return dest;   
    14. }  

    借助于一个临时缓冲区temp,即使src和dest所指的内存区间有重叠也能正确拷贝。

    4、连接字符串

    1. #include <string.h>   
    2. char *strcat(char *dest, const char *src);   
    3. char *strncat(char *dest, const char *src, size_t n);   
    4. //返回值:dest指向哪,返回的指针就指向哪  

    strcat把src所指的字符串连接到dest所指的字符串后面,例如:

    1. char d[10] = "foo";   
    2. char s[10] = "bar";   
    3. strcat(d, s);   
    4. printf("%s %s\n", d, s);  

    调用strcat函数后,缓冲区s的内容没变,缓冲区d中保存着字符串"foobar",注意原来"foo"后面的'\0'被连接上来的字符串"bar"覆盖掉了,"bar"后面的'\0'仍保留。

    strcat和strcpy有同样的问题,调用者必须确保dest缓冲区足够大,否则会导致缓冲区溢出错误。strncat函数通过参数n指定一个长度,就可以避免缓冲区溢出错误。注意这个参数n的含义和strncpy的参数n不同,它并不是缓冲区dest的长度,而是表示最多从src缓冲区中取n个字符(不包括结尾的'\0')连接到dest后面。如果src中前n个字符没有出现'\0',则取前n个字符再加一个'\0'连接到dest后面,所以strncat总是保证dest缓冲区以'\0'结尾,这一点又和strncpy不同,strncpy并不保证dest缓冲区以'\0'结尾。所以,提供给strncat函数的dest缓冲区的大小至少应该是strlen(dest)+n+1个字节,才能保证不溢出。

    5、比较字符串

    1. #include <string.h>   
    2. int memcmp(const void *s1, const void *s2, size_t n);   
    3. int strcmp(const char *s1, const char *s2);   
    4. int strncmp(const char *s1, const char *s2, size_t n);   
    5. //返回值:负值表示s1小于s2,0表示s1等于s2,正值表示s1大于s2  

    memcmp从前到后逐个比较缓冲区s1和s2的前n个字节(不管里面有没有'\0'),如果s1和s2的前n个字节全都一样就返回0,如果遇到不一样的字节,s1的字节比s2小就返回负值,s1的字节比s2大就返回正值。

    strcmp把s1和s2当字符串比较,在其中一个字符串中遇到'\0'时结束,按照上面的比较准则,"ABC"比"abc"小,"ABCD"比"ABC"大,"123A9"比"123B2"小。

    strncmp的比较结束条件是:要么在其中一个字符串中遇到'\0'结束(类似于strcmp),要么比较完n个字符结束(类似于memcmp)。例如,strncmp("ABCD", "ABC", 3)的返回值是0,strncmp("ABCD", "ABC", 4)的返回值是正值。

    1. #include <strings.h>   
    2. int strcasecmp(const char *s1, const char *s2);   
    3. int strncasecmp(const char *s1, const char *s2, size_t n);   
    4. //返回值:负值表示s1小于s2,0表示s1等于s2,正值表示s1大于s2  

    这两个函数和strcmp/strncmp类似,但在比较过程中忽略大小写,大写字母A和小写字母a认为是相等的。这两个函数不属于C标准库,是POSIX标准中定义的。

    6、搜索字符串

    1. #include <string.h>   
    2. char *strchr(const char *s, int c);   
    3. char *strrchr(const char *s, int c);   
    4. //返回值:如果找到字符c,返回字符串s中指向字符c的指针,如果找不到就返回NULL  

    strchr在字符串s中从前到后查找字符c,找到字符c第一次出现的位置时就返回,返回值指向这个位置,如果找不到字符c就返回NULL。strrchr和strchr类似,但是从右向左找字符c,找到字符c第一次出现的位置就返回,函数名中间多了一个字母r可以理解为Right-to-left。

    1. #include <string.h>   
    2. char *strstr(const char *haystack, const char *needle);   
    3. //返回值:如果找到子串,返回值指向子串的开头,如果找不到就返回NULL  

    strstr在一个长字符串中从前到后找一个子串(Substring),找到子串第一次出现的位置就返回,返回值指向子串的开头,如果找不到就返回NULL。这两个参数名很形象,在干草堆haystack中找一根针needle,按中文的说法叫大海捞针,显然haystack是长字符串,needle是要找的子串。

    7、分割字符串

    很多文件格式或协议格式中会规定一些分隔符或者叫界定符(Delimiter),例如/etc/passwd文件中保存着系统的帐号信息:

    1. $ cat /etc/passwd   
    2. root:x:0:0:root:/root:/bin/bash   
    3. daemon:x:1:1:daemon:/usr/sbin:/bin/sh   
    4. bin:x:2:2:bin:/bin:/bin/sh   
    5. ...  

    每条记录占一行,也就是说记录之间的分隔符是换行符,每条记录又由若干个字段组成,这些字段包括用户名、密码、用户id、组id、个人信息、主目录、登录Shell,字段之间的分隔符是:号。解析这样的字符串需要根据分隔符把字符串分割成几段,C标准库提供的strtok函数可以很方便地完成分割字符串的操作。tok是Token的缩写,分割出来的每一段字符串称为一个Token。

    1. #include <string.h>   
    2. char *strtok(char *str, const char *delim);   
    3. char *strtok_r(char *str, const char *delim, char **saveptr);   
    4. //返回值:返回指向下一个Token的指针,如果没有下一个Token了就返回NULL  

    参数str是待分割的字符串,delim是分隔符,可以指定一个或多个分隔符,strtok遇到其中任何一个分隔符就会分割字符串。看下面的例子。

    例 7.1. strtok

    1. #include <stdio.h>   
    2. #include <string.h>   
    3.   
    4. int main(void)   
    5. {   
    6.     char str[] = "root:x::0:root:/root:/bin/bash:";   
    7.     char *token;   
    8.     token = strtok(str, ":");   
    9.     printf("%s\n", token);   
    10.     while ( (token = strtok(NULL, ":")) != NULL)   
    11.     printf("%s\n", token);   
    12.   
    13.     return 0;   
    14. }  
    1. $ ./a.out   
    2. root   
    3. x   
    4. 0   
    5. root   
    6. /root   
    7. /bin/bash  

    结合这个例子,strtok的行为可以这样理解:冒号是分隔符,把"root:x::0:root:/root:/bin/bash:"这个字符串分隔成"root"、"x"、""、"0"、"root"、"/root"、"/bin/bash"、""等几个Token,但空字符串的Token被忽略。第一次调用要把字符串首地址传给strtok的第一个参数,以后每次调用第一个参数只要传NULL就可以了,strtok函数自己会记住上次处理到字符串的什么位置(显然这是通过strtok函数中的一个静态指针变量记住的)。

  • 相关阅读:
    nginx内置高可用配置与第三方高可用模块nginx_ustream_check_mudule配置
    nginx+php,nginx+tomcat动静分离实战
    面向对象三大特性之多态与多态性
    面向对象三大特性之组合
    面向对象三大特性之继承与派生
    面向对象编程初探
    常用模块
    模块
    函数
    文件处理
  • 原文地址:https://www.cnblogs.com/herizai/p/3130256.html
Copyright © 2011-2022 走看看