C语言字符串库函数详解
1.strlen函数:求字符串长度
int main()
{
char arr1[] = "abc";
char arr2[] = { 'a', 'b', 'c' };
int len1 = my_strlen(arr1);
int len2 = strlen(arr2);
printf("%d\n", len1);
//输出3,字符串长度不包括"\0"
printf("%d\n", len2);
//输出随机值,因为字符串arr2没有'\0',strlen找不到终点
return 0;
}
1.1 模拟实现strlen函数
int my_strlen(const char* str)//使用const让指针修饰的内容无法被改变
{
int count = 0;
assert(str != NULL);
while(*str != '\0')
{
count++;
str++;
}
return count;
}
1.2 关于strlen函数的1个习题
int main()
{
const char*str1 = "abcdef";
const char*str2 = "bbb";
if (strlen(str2) - strlen(str1) > 0)
{
printf("str2>str1\n");
}
else
{
printf("srt1>str2\n");
}
return 0;
}
输出"str2>str1",因为strlen返回类型size_t是个无符号数,理论上是3-6=-3,但是-3会被解析为无符号数,所以答案是>
2.strcpy函数:字符串拷贝
char arr[20] = { 0 };
strcpy(arr,"hello");
//arr必须是个数组而不能是个字符串常量,只有可修改的destination才能承受拷贝来的字符串,所以此处必须声明为数组
char arr2[] = { 'a', 'b', 'c' };
strcpy(arr, arr2);
这里没有’\0’,而’\0’是strcpy的终止条件,所以此处会报错,拷贝找不到终止的位置
char arr[2] = { 0 };
strcpy(arr, "hello");
可以拷贝,但是拷贝完会出错,目标空间需要足够大才能承受我们拷贝过去的数据,此程序会报错
3.strcat函数:字符串追加
char arr[20] = "hello ";
strcat(arr,"world");
printf("%s\n", arr);
要求目标要有足够大的空间用来追加那个想追加的字符串,所以此处arr声明的空间20比较大;
char arr[20] = "hello \0####";
strcat(arr, "world");
printf("%s\n", arr);
用来测试world里面的\0是否会被拷贝来,测试结果是world里面的\0会被拷贝进来
3.1 strcat库函数的使用
int main()
{
char arr[20] = "abcd";
strcat(arr, arr);//错了,不能自己给自己追加,因为\0被改变了,找不到结束的标志\0了
}
需要注意strcat函数不能自己给自己追加,上面的代码会报错
3.2 strcat库函数的模拟实现
实现过程分为2个步骤:(1)找到目标字符串中的\0(2)把源字符串加上去,包括\0
char* my_strcat(char* dest,const char* src)//库函数中的返回类型是char*,是指向目标空间的地址
{
char* ret = dest;
assert(dest&&src);//两个都不能是空指针
//1.找到目标字符串中的\0
while (*dest)
{
dest++;
}
//while循环停下来的时候,dest指向'\0'
//2.追加源字符串,包含'\0'
while (*dest++= *src++)
{
;
}
return ret;//返回目标空间的起始地址
}
4.strcmp函数:字符串比较
比较2个字符串是否相等,注意strcmp比较的是对应字符的asc码值的大小,而不是比较字符串长度大小
4.1 strcmp函数的使用
int main()
{
char* p = "abcdef";
char* q = "abbb";
int ret = strcmp(p, q);
if (ret > 0)
printf("p>q\n");
else if (ret == 0)
printf("p=q\n");
else
printf("p<q\n");
return 0;
}
标准规定:
第一个字符串大于第二个字符串,则返回大于0的数字
第一个字符串等于第二个字符串,则返回0
第一个字符串小于第二个字符串,则返回小于0的数字
4.2 strcmp函数的模拟实现
方法一:
int my_strcmp(const char* str1, const char* str2)
{
while (*str1 == *str2)
{
if (*str1 == '\0')//此处考虑str1为'\0'的特殊情况
{
return 0;
}
str1++;
str2++;
}
if (*str1 > *str2)
{
return 1;
}
else
{
return -1;
}
}
方法二:由于库函数中仅仅返回正值、负值或者0,所以方法一中返回1、-1、0来模拟是不严谨的,方法二是对方法一的优化
int my_strcmp2(const char* str1, const char* str2)
{
while (*str1 == *str2)
{
if (*str1 == '\0')
{
return 0;
}
str1++;
str2++;
}
return *str1 - *str2;//此处不一定要返回1,0,-1;而是直接返回正数,0或负数
}
上面所说的strcpy,strcat,strcmp都是长度不受限制的字符串函数;而接下来要说的strncpy,strncat,strncmp都是长度受限制的字符串函数
5.strncpy函数:长度受限制的字符串拷贝
char * strncpy(char * destination, const char * source, size_t num);
拷贝num个字符从源字符串到目标空间。如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个
5.1 strncpy函数的使用
int main()
{
char arr1[20] = "abcdef";
char arr2[] = "qwer";
strncpy(arr1, arr2, 2);
printf("%s\n", arr1);//打印qwcdef
return 0;
}
int main()
{
char arr1[20] = "abcdefghi";
char arr2[] = "qwer";
strncpy(arr1, arr2, 6);//确实会拷贝6个过去,但最后2个字符是0和\0,qwer0\0
printf("%s\n", arr1);//qwer
return 0;
}
6.strncat函数:限制长度的字符串追加
6.1strncat函数的使用
int main()
{
char arr1[20] = "hello ";
char arr2[] = "world";
strncat(arr1, arr2, 3);
printf("%s\n", arr1);//hello wor\0,主动再末尾补上\0
return 0;
}
追加字符数量是3,所以只在"hello “后面加上"wor”
int main()
{
char arr1[20] = "hello ";
char arr2[] = "world";
strncat(arr1, arr2, 6);
printf("%s\n", arr1);//hello world\0
return 0;
}
追加字符数量为6,输出"hello world"
int main()
{
char arr1[20] = "hello ";
char arr2[] = "world";
strncat(arr1, arr2, 10);
printf("%s\n", arr1);//hello world\0
return 0;
}
追加字符数量为10,仍然输出"hello world"
7.strncmp函数:比较2个字符串前num个字符是否相等
7.1 strncmp函数的使用
int main()
{
char arr1[20] = "hello ";
char arr2[] = "world";
int ret=strncmp(arr1, arr2, 3);//比较arr1和arr2的前3个字符是否相等
printf("%d\n", arr1);//hello world\0
return 0;
}
8.strstr函数:字符串查找
8.1 strstr函数的使用
int main()
{
char arr1[] = "abcdefabcdef";
char arr2[] = "bcd";
//在arr1中查找arr2
char* ret = strstr(arr1, arr2);
if (ret == NULL)
printf("没找到\n");
else
printf("找到了,%s\n",ret);
}
返回str2在str1中第一次出现的位置,如果str1中没有str2,则返回空指针
8.2 strstr函数的模拟实现
char* my_strstr(const char*str1,const char*str2)
{
assert(str1&&str2);
const char* s1 = str1;
const char* s2 = str2;
const char *cp = str1;//cp指针用来标记str1开始查找的起始位置
if (str2 == '\0')
return (char*)str1;
while (*cp)
{
s1 = cp;
s2 = str2;
while (*s1&&*s2&&(*s1 == *s2))
{
s1++;
s2++;
}
if (*s2 == '\0')
{
return (char*)cp;
}
cp++;
}
}
9.strtok函数:用来切割字符串
9.1 strtok函数的使用
int main()
{
char arr[] = "abc@abcd.com";
char* p = "@.";
//p中可以放上所有的分隔符
char temp[20] = { 0 };
//strtok会把分隔符所在位置变成\0,再返回前面那个字符串的地址,切割这个字符串会被修改,所以会再创建一个该字符串的拷贝用于修改
strcpy(temp, arr);
char* ret = NULL;
ret=strtok(temp, p);
//strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
ret=strtok(NULL, p);
//strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
ret=strtok(NULL, p);
//下面一段循环是对上面的简化
for (ret = strtok(temp, p); ret != NULL;ret=strtok(NULL,p))
//当ret返回空指针跳出循环,传空指针时会从上一次记住的位置开始找,该函数具有记忆功能,该函数内部可能会有static变量
{
printf("%s\n", ret);
}
return 0;
}
10.strerror函数:翻译错误码
使用库函数调用失败时都会设置错误码,如C语言中的错误码是int errno,而strerror则会把错误码翻译成错误信息
10.1 strerror函数的使用
#include<errno.h>
int main()
{
printf("%s\n", strerror(0));
printf("%s\n", strerror(1));
printf("%s\n", strerror(2));
printf("%s\n", strerror(3));
printf("%s\n", strerror(4));
//不同错误码对应不同错误信息
FILE* pf = fopen("test.txt", "r");
//打开文件,"r"表示read,读取
if (pf == NULL)
{
printf("%s\n", strerror(errno));
//翻译错误信息,即文件不存在----"No such file or dictinary"
return 1;
}
fclose(pf);//关闭文件
pf = NULL;
return 0;
}