以下函数的使用均需要包含头文件
求字符串长度函数 | strlen |
---|---|
长度不受限制的字符串函数 | strcpy strcat strcmp |
长度受限制的字符串函数 | strncpy strncat strncmp |
字符串查找函数 | strstr strtok |
错误信息报告函数 | strerror |
在C语言中,对字符串的处理是十分频繁的。但是,C语言本身是没有字符串类型的,通常字符串被存放在字符数组和常量字符串中。唯一标识字符串的结束标志是**‘\0’**字符。
补充:常量字符串通常存放在内存中的文字常量区内,常量字符串的内容不可以被修改。
strlen函数是用来获取’\0’字符前,字符的个数,即求字符串的长度。
size_t strlen(const char* str);
通过查阅文档可知,该函数的参数为 const char* str。即一个const修饰字符指针变量,该字符指针变量的指向内容不可以被修改。该函数返回值为size_t类型。也就是无符号整型,即unsigned int类型。
注意事项:
1、字符串以’\0’作为结束标识,参数指向的字符串必须以’\0’作为结束标识。
2、返回值size_t为无符号整数
下面我通过一个简单的例子,使用库函数strlen求一个字符串的长度。
#include
#includeint main()
{//创建一个字符数组str1char str1[] = "abcde";//创建一个字符指针pc//pc指向常量字符串xyz首字符的地址 char* pc = "xyz";//创建临时变量存放strlen函数的返回值size_t ret1 = strlen(str1);size_t ret2 = strlen(pc);//打印返回值验证结果printf("%u\n" ,ret1);printf("%u\n", ret2);return 0;
}
根据之前对于函数章节的学习,也可以通过函数的链式访问来进行对于strlen函数的使用,代码例子如下。
#include
#includeint main()
{//创建一个字符数组str1char str1[] = "abcde";//创建一个字符指针pc//pc指向常量字符串xyz首字符的地址 char* pc = "xyz";//通过函数链式访问打印返回值验证结果printf("%u\n" ,strlen(str1));printf("%u\n", strlen(pc));return 0;
}
补充:打印无符号整数的占位符为%u,当前在作者所使用的vs2019中x86环境下,%d格式打印编译器并不会警告,但是这不意味着%d打印格式就是没有问题的。从语法角度来看%u打印格式才是正确合理的。
方法一:计数器方法
实现思路:创建一个临时变量来记录’\0’字符前的字符个数。通过while循环来进行解引用后的字符判断,只要解引用访问的字符不是’\0’,就让指针向后移动,同时计数器+1。
#include
#includesize_t my_strlen(const char* str)
{//断言判断指针有效性assert(str != NULL);//创建临时变量count记录字符串的长度size_t count = 0;//当*str == '\0'时结束循环//'\0'的ASCII值为0//在C语言中0为假非0为真while(*str){count++;str++;}return count;
}int main()
{char str1[] = "abcd";printf("%u\n",my_strlen(str1));return 0;
}
方法二:指针运算
实现思路:创建一个临时指针变量存放字符串的起始地址,然后循环遍历字符串内容,当指针解引用访问后得到的字符为’\0’,循环停止。通过指针减去指针,得到指针间的元素个数从而得到字符串的长度。
#include
#includesize_t my_strlen(const char* str)
{//断言判断指针有效性assert(str);//const修饰sta使左右类型匹配const char* sta = str;while (*str){str++;}//指针-指针返回'\0'前元素个数return str - sta;
}int main()
{char* pc = "abcdef";size_t ret = my_strlen(pc);printf("%u\n",ret);return 0;
}
方法三:递归
实现思路:将字符串结束标志’\0’作为递归终止条件,判断str指针解引用访问的字符内容,当str指针解引用访问内容不为’\0’,递推使返回值+1并且调用函数体自身,参数为str指针+1。当*str == ‘\0’,递推终止,进行回归,返回字符串长度。
#include
#includesize_t my_strlen(const char* str)
{//断言判断指针有效性assert(str);if(*str == '\0')return 0;elsereturn 1 + my_strlen(str+1);
}int main()
{char* pc = "abc";size_t ret = my_strlen(pc);printf("%u\n",ret);return 0;
}
将源头字符串的内容拷贝到目标字符串当中,包含’\0’字符。
char* strcpy(char* destination, const char* source);
该函数的返回类型是char* 类型即字符指针变量,返回的目标空间的起始地址。该函数的参数有两个分别是char* destination,即字符指针变量,该指针变量存放的是目标字符串的首字符地址。const char* source ,const修饰过的字符指针变量,该指针变量的所指向的内容不可以被修改。该指针变量存放的是拷贝源字符串的内容的首字符地址。
注意事项:
1、源头字符串必须以’\0’结尾(若没有’\0’作为结束标志,strcpy就不知道什么时候停止拷贝)
2、源头字符串的’\0’会被拷贝到目标字符串中。
3、目标字符串的空间必须足够大,否则会造成越界访问。
4、目标字符串必须可修改(不能被const修饰)
#include
#includeint main()
{char str1[20]={0};char str2[] = "abcdef";//将str2的内容拷贝到str1中strcpy(str1,str2);printf("%s\n", str1);return 0;
}
实现思路:创建指针变量存放目标字符串的起始地址,通过循环将源头字符串中的内容赋给目标字符串,直到将’\0’赋给目标字符串后,循环终止,返回目标字符串的地址。
char* my_strcpy(char* dest, const char* src)
{//断言判断指针合法性assert(dest && src);//记录目标地址char* ret = dest;//当*dest为'\0'时,循环结束,拷贝停止while (*dest++ = *src++){;}return ret;
}int main()
{char str1[20] = { 0 };char str2[] = "hello world";//将str2的内容拷贝到str1中my_strcpy(str1, str2);printf("%s\n", str1);return 0;
}
将源头字符串的内容追加到目标字符串。
char* strcat(char* destination, const char* source);
这里的返回类型和参数跟strcpy一模一样,这里我就不多做赘述。
注意事项:
1、源头字符串从目标字符串的’\0’处开始追加。
2、目标字符串的大小必须确保能够放的下源头字符串。
3、源头字符串必须以’\0’结束,且会将源头字符串的’\0’拷贝到目标空间中。
4、目标字符串必须可修改(未被const修饰)
#include
#includeint main()
{char str1[20] = "abc";char str2[] = "def";//将str2追加到str1中strcat(str1,str2);printf("%s\n", str1);return 0;
}
实现思路:先创建临时指针变量存放起始位置的地址,然后遍历目标字符串,直到目标字符串的指向空间内容为’\0’,最后追加源字符串到目标字符串中。返回目标字符串的地址。
char* my_strcat(char* dest, const char* src)
{assert(dest && src);//记录目标字符串起始位置char* ret = dest;//判断dest指针指向的内容是否为'\0'//不为*dest != '\0',指针++while (*dest){dest++;}//追加字符串(实现过程和字符串拷贝一样)while (*dest++ = *src++){;}return ret;
}int main()
{char str1[20] = "hello ";char str2[] = "world";//将str2追加到str1中my_strcat(str1, str2);printf("%s\n", str1);return 0;
}
用于比较两个字符串的大。
int strcmp ( const char * str1, const char * str2 );
函数的返回类型为整型,函数的参数为两个被const修饰的字符指针变量。
注意事项:
1、两个字符比较,其实比较的是两个字符在内存中存储的ASCII码值。
2、当第一个字符串大于第二个字符串时,返回一个大于0的整数。
3、当第一个字符串等于第二个字符串时,返回一个等于0的整数。
4、当第一个字符串小于第二个字符串时,返回一个小于0的整数。
#include
#includeint main()
{char str1[] = "abc";char str2[] = "abc";//存放strcmp函数的返回值int ret = strcmp(str1, str2);if(ret > 0)//根据返回值判断字符串大小printf("str1 > str2\n");else if(ret < 0)printf("str1 < str2\n");elseprintf("str1 == str2\n");return 0;
}
实现思路:通过解引用操作访问两个指针指向的内容进行比较,当两个指针变量解引用访问的值相等,则两个指针偏移一个字节继续向后访问,若碰到’\0’或者指针解引用访问的值不相等则返回一个整数。
#include
#includeint my_strcmp(const char* str1, const char* str2)
{int ret = 0;//断言判断指针有效性assert(str1 && str2);//循环判断两个字符是否相等while(*str1 == *str2){if(str1 == '\0')return 0;str1++;str2++;}return *str1 - *str2;
}int main()
{char str1[] = "abc";char str2[] = "abd";int ret = my_strcmp(str1,str2);if(ret > 0)//根据返回值判断字符串大小printf("str1 > str2\n");else if(ret < 0)printf("str1 < str2\n");elseprintf("str1 == str2\n");return 0;
}
由于不受长度限制的字符串库函数使用起来相对受长度限制的字符串库函数来说比较的不安全,所以C语言标准库中也提供了受长度限制的库函数。这里我就举三个常用的例子
追加n个字节的源头字符串到目标字符串后
char * strncat ( char * destination, const char * source, size_t num );
函数返回的依旧是目标字符串的起始地址。参数部分额外多了一个参数size_t num,即拷贝的字符个数。
注意事项:
1、将源头字符串的起始位置起num个字符的内容追加到目标字符串的’\0’处
2、如果源头字符串的长度小于num,那么只追加源头字符串到其’\0’处。
#include
#includeint main()
{char str1[20] = "abcde";char str2[] = "fghijk";strncat(str1,str2,3);printf("%s\n", str1);return 0;
}
实现思路:先创建临时指针变量存放起始位置的地址,然后遍历目标字符串,直到目标字符串的指向空间内容为’\0’,最后,追加num个字符的源字符串到目标字符串中。返回目标字符串的地址。
#include
#includechar* my_strncat(char* dest, const char* src, size_t num)
{//记录目标字符串起始位置char* ret = dest;//断言判断指针有效性assert(dest && src);//先遍历目标字符串,使指针变量dest指向'\0'while(*dest){dest++;`在这里插入代码片`}//通过num控制追加的字节数while(num--){//追加字符串(同拷贝字符串)*(dest+num) = *(src+num);}return ret;
}int main()
{char str1[20] = "abcde";char str2[] = "fghijk";printf("%s\n", my_strncat(str1,str2,4));return 0;
}
拷贝num个字符的源头字符串内容到目标字符串中。
char * strncpy ( char * destination, const char * source, size_t num );
返回类型依旧是目标字符串的起始位置。参数方面,前俩个参数不变,第三个参数为num个字符的空间。
注意事项:
1、将源头字符串的第一个字符数复制到目标字符串中。如果在复制 num 个字符之前找到源 头字符串的末尾(由 ‘\0’ 字符表示),则目标将填充0,直到总共写入 num 个字符为止。
2、如果源字符串的长度超过 num,则不会在目标字符串末尾附加’\0’字符。因此,在这种情况下,不应将目标字符串视为以’\0’结尾的字符串(这样读取它会溢出)。
3、目标字符串和源头字符串不得重叠(有关重叠时更安全的替代方案,请参见 Memmove)。
#include
#includeint main()
{char str1[20] = {0};char* pc = "hello world";strncpy(str1, pc, 11);printf("%s\n", str1);return 0;
}
设计思路:创建指针变量存放目标字符串的起始地址,通过循环将num个源头字符串中的字符赋给目标字符串,,当赋完num个字符时,循环终止,返回目标字符串的地址。
#include
#includechar* my_strncpy(char* dest, const char* src, size_t num)
{char* ret = dest;assert(dest && src);//拷贝num个字符while (num--){//将源头字符串字符拷贝到目标字符串中*(dest + num) = *(src + num);}return ret;
}int main()
{char str1[20] = { 0 };char* pc = "hello world";printf("%s\n", my_strncpy(str1, pc, 3));return 0;
}
比较两个字符串的n个字符的ASCII码值大小
int strncmp ( const char * str1, const char * str2, size_t num );
strncmp函数的前两个参数是需要进行比较的两个字符串,第三个是比较的字符个数,num是几就比较几个字符。
注意事项:
1、比较字符串str1和字符串str2的最多num个字符。
2、这个函数开始比较每个字符串的第一个字符。如果它们彼此相等,则继续处理下面的对,直到字符不同,直到到达一个’\0’字符,或直到两个字符串中num字符匹配,以先发生的为准。
#include
#includeint main()
{char str1[] = "abcd";char str2[] = "abcq";int ret = strncmp(str1, str2, 3);printf("%d\n", ret);return 0;
}
实现思路:通过num来控制循环变量,判断两个两个字符串每个字符的内容是否相等,循环结束后,返回最终两个字符的差。
#include
#includeint my_strncmp(const char* str1, const char* str2, size_t num)
{assert(str1 && str2);//num控制循环次数while (num--){//比较两个字符if (*str1 == *str2){str1++;str2++;}}return *str1 - *str2;
}int main()
{char str1[] = "abcd";char str2[] = "abcq";int ret = strncmp(str1, str2, 4);printf("%d\n", ret);return 0;
}
查找字符串中的字符串,并返回子字符串的起始地址
const char * strstr ( const char * str1, const char * str2 );
char * strstr ( char * str1, const char * str2 );
返回类型为子字符串的起始地址,当子字符串不存在时,返回一个空指针。参数str1为被查找的字符串,参数str2为需要查找的字符串。
注意事项:
strstr函数匹配子字符串的过程是不包含’\0’的,但是查找的字符串已是’\0’,那么函数便停止查找,并返回子字符串起始地址。
#include
#includeint main()
{char str1[] = "abbbcde";char str2[] = "bbc";printf("%s\n", strstr(str1, str2));return 0;
}
#include
#includeint main()
{char str1[] = "abbbcde";char str2[] = "be";printf("%s\n", strstr(str1, str2));return 0;
}
实现思路:首先需要两个指针变量s1和s2指向两个字符串的起始地址。再创建一个临时指针变量cp来记录str2在str1中首次出现的位置。当str2传进来是空字符串(‘\0’)时,直接返回str1的起始地址。通过循环来遍历两个字符串。当cp指针指向的内容为’\0’时循环结束,内循环判断 *s1 是否与 *s2 相等。若 *s2 == '\0’则返回cp指针,即记录str2在str1中首次出现的地址。若循环结束还未匹配则返回NULL,即空指针。
#include
#includeconst char* my_strstr(const char* str1, const char* str2)
{const char* s1 = NULL;const char* s2 = NULL;const char* cp = str1;assert(str1 && str2);if (*str2 == '\0')return str1;//子字符串匹配条件while (*cp){s1 = cp;s2 = str2;//只有s1 == s2且都不为'\0',此时匹配是否为子字符串while ((*s1 != '\0') && (*s2 != '\0') && (*s1 == *s2)){s1++;s2++;}//子字符串匹配成功if (*s2 == '\0')return cp;cp++;}//未查找到子字符串return NULL;
}int main()
{const char str1[] = "abbbcde";const char str2[] = "bbc";const char str3[] = "abbbcde";const char str4[] = "be";printf("%s\n", my_strstr(str1, str2));printf("%s\n", my_strstr(str3, str4));return 0;
}
根据分割符字符串,分割指定字符串的内容,如:给定待分割字符串str1[] = “zhangsan@qq.com”,给定分割符字符串str2[] = “@.”。此时调用strtok(str1,str2);函数将会把str1中含有分隔符字符串的位置覆盖成’\0’,并返回切割后各个子串首字符的地址。
char * strtok ( char * str, const char * delimiters );
strtok函数的返回类型是字符指针,指针中存放的是分割后各个子串的地址。strtok函数的参数是两个字符指针,第一个是用来存放待分割的字符串地址,第二个是用来存放分割符的集合地址。
注意事项:
1、strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容 并且可修改。
2、若strtok函数的第一个参数不为 NULL ,函数将找到字符串中第一个标记,strtok函数将保存它在字符串中的位置。
3、strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
4、如果字符串中不存在更多的标记,则返回 NULL 指针。
#include
#includeint main()
{ char str1[] = "zhangsan@qq.com";char str2[50] = { 0 };char del[] = "@.";//用于控制循环,并存放strtok函数的返回值char* ret = NULL;strcpy(str2, str1);//循环打印分割后的字符串for (ret = strtok(str2, del); ret != NULL; ret = strtok(NULL, del)){printf("%s ",ret);}printf("\n");return 0;
}
返回错误码所对应的错误信息
char * strerror ( int errnum );
strerror函数的返回类型是char* 的指针变量,该指针变量存放的是所对应的错误信息的首字符地址。函数的参数为错误信息码,即errno全局变量。
注意事项:
使用errno全局变量需要引相应的头文件。
#include
#include
#includeint main()
{//打开文件FILE* pf = fopen("test.text","r");if (NULL == pf){printf("%s\n",strerror(errno));return 1;}//关闭文件fclose(pf);pf = NULL;return 0;
}