前言:在编程的过程中,我们经常要处理字符和字符串,为了⽅便操作字符和字符串,C语⾔标准库中提供了 ⼀系列库函数,接下来我们就学习⼀下这些函数。
目录
1. 字符函数
1.1 字符分类判断函数
1.2 字符转换函数
1.3 练习
2. 字符串函数
2.1 常用函数的 使用和模拟实现
1. strlen函数【计算字符串长度】
2. strcpy函数【拷贝字符串】
3. strcat函数【拼接字符串】
4. strcmp函数【比较两个字符串大小】
5. strchr函数【在字符串中找一个字符】
6. strstr函数【在字符串中找一段字符】
2.2 其他字符串函数的 使用规则
7. strncpy函数
8. strncat函数
9. strncmp函数
10. strrchr函数
11. strtok函数
12. strerror函数 与 perror函数
1. 字符函数
C语⾔中有⼀系列的函数是专⻔做字符分类的,也就是⼀个字符是属于什么类型的字符的。 这些函数的使⽤都需要包含⼀个头⽂件是 <ctype.h>
1.1 字符分类判断函数
这11个字符函数的函数原型,(1)除了名字不同,(2)它们的返回值都是int(且都是0或1),(3)参数表中也只有一个int型的形参。
比如:
函数原型:int islower(int c);
作用:如果输入c的ascii值属于a~z,那么返回值为1,否则返回值为0。
1.2 字符转换函数
C语⾔提供了2个字符转换函数:(只有2个)
函数原型:
int tolower ( int c ); //将参数传进去的⼤写字⺟转⼩写
int toupper ( int c ); //将参数传进去的⼩写字⺟转⼤写
tupper函数的模拟实现:
int my_toupper(int c)
{
if (c >= 'a' && c <= 'z')
{
c = c - 32;
}
return c;
}
1.3 练习
写⼀个代码,将字符串中的⼩写字⺟转⼤写,其他字符不变:
int main()
{
int i = 0;
char str[] = "Test String.\n";
char c;
while (str[i])
{
c = str[i];
if (islower(c))
c = toupper(c);
putchar(c);
i++;
}
return 0;
}
2. 字符串函数
C语音中也有一系列的字符串函数,使用时需要包含头文件<string.h>
补充:下面的模拟实现中我用了assert断言,保证传入的多个字符串地址不是NULL,使用assert要包含头文件<assert.h>
2.1 常用函数的 使用和模拟实现
1. strlen函数【计算字符串长度】
函数原型:
1. size_t strlen ( const char * str );
作用:获取字符串长度。
使用规则:
- strlen函数返回的是在字符串中 '\0' 前⾯出现的字符个数(不包含 '\0' )。
- 字符串以 '\0' 作为结束标志,参数指向的字符串必须要以 '\0' 结束。
- 注意函数的返回值为size_t,是⽆符号的( 易错 )
补充:sizeof 对于字符\0也会计算,而strlen不会。比如字符串“hello\0w”,sizeof计算的结果是8(别忘了w后面还有\0),而strlen计算的结果是5。
strlen的模拟实现:
strlen有3种方法可以模拟实现:
版本1
strlen--“计算器累计”版
int my_strlen1(const char* str)
{
assert(str);
int count = 0;
char* p = str;
while (*p)
{
count++;
p++;
}
return count;
}
const修饰的原因:
保证了:不能通过解引用*str对原字符串进行修改
版本2
strlen--“指针-指针”版
int my_strlen2(const char* str)
{
assert(str);
char* p = str;
while (*p)
p++;
return p - str;
}
版本3
strlen--“函数递归”版
int my_strlen3(const char* str)
{
if (*str == '\0')
return 0;
else
return 1 + my_strlen3(str + 1);
}
函数递归也可以写成这样:
int my_strlen3(const char* str)
{
return *str=='\0' ? 0 : 1+my_strlen3(str+1);
}
2. strcpy函数【拷贝字符串】
函数原型:
1. char* strcpy(char * destination, const char * source );
作用:把字符串source的内容复制到字符串destination中。
使用规则:
- source是源字符串,源字符串必须以 '\0' 结束。
- 会将源字符串中的 '\0' 也拷⻉到⽬标空间。
- ⽬标空间必须足够⼤,以确保能存放源字符串。
- ⽬标空间必须可修改。
strcpy的模拟实现:
char* my_strcpy(char* dest, const char* src)
{
assert(dest && src);
char* ret = dest;
while (*dest++ = *src++); //会赋值为'\0'后再判断
return ret;
}
while表达式的解析:
(1)先对两个指针进行+1操作
(2)然后对两边+1前的地址值进行解引用操作
(3)再把src所指向的内容赋值给dest
(4)最后对+1后的*dest检查是否等于'\0'(数值上,'\0'就等于0)
为了方便理解,可以写成这样:
while(*src != '\0')
{
*dest = *src;
src++;
dest++;
}
3. strcat函数【拼接字符串】
函数原型:
1. char* strcat(char * destination, const char * source );
作用:将一个字符串source追加到另一个字符串destination的末尾
使用规则:
- 源字符串必须以 '\0' 结束。
- 会将源字符串中的 '\0' 拷⻉到⽬标空间。
- ⽬标空间必须⾜够⼤,以确保能存放源字符串。
- ⽬标空间必须可修改。
strcat的模拟实现:
char* my_strcat(char* dest, const char* src)
{
assert(dest && src);
char* ret = dest;
while (*dest)
{
dest++;
}
while (*dest++ = *src++); //会赋值为'\0'后再判断
return ret;
}
解析:
(1)第一个while循环,让指针dest指向目标字符串的结尾'\0'处。
(2)从dest指向的\0处开始拼接,并把\0覆盖掉。
(3)第二个while循环与strcpy的拷贝一样。
4. strcmp函数【比较两个字符串大小】
函数原型:
1. int strcmp(const char *srt1, const char *str2);
作用:比较str1与str2的大小
使用规则:
- str1⼤于str2,则返回⼤于0的数字 ◦
- str1等于str2,则返回0 ◦
- str1小于str2,则返回小于0的数字 ◦
- 比较的本质1:⽐较两个字符串中对应位置上字符ASCII码值的大小。
- 比较的本质2:比较出第1次不相同的两个字母,如果都相同才返回0。
strcmp的模拟:
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);
while (*str1 && *str2 && *str1 == *str2 )
{
str1++;
str2++;
}
return *str1 - *str2;
}
其实也可以写成这样:
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);
while (*str1 == *str2)
{
if (*str1 == '\0')
return 0;
str1++;
str2++;
}
return *str1 - *str2;
}
在“*str1==*str2”的前提下,如果满足*str1==‘\0’,那么也满足*str2==‘\0’。此时整个字符串已经比较完了,并没有不同的字符,所以返回0。
5. strchr函数【在字符串中找一个字符】
函数原型:
1. char *strchr(const char *str, int c);
作用:在一个字符串中查找给定字符的第一个匹配之处
使用规则:
str
是要查找的字符串,c
是要查找的字符(输入时要acsii的值)- 从字符串的左边开始查找。
- 如果找到第一个与c对应的字符,则返回该字符的地址(指针)。
- 如果没有,则返回NULL。
strchr的模拟实现:
char* my_strchr(const char* str, int c)
{
assert(str);
char* pos = NULL;
while (*str)
{
if (*str == c)
{
pos = str;
break;
}
str++;
}
return pos;
}
如果没有找到与c一样的字符,那么pos仍然为NULL,此时返回pos就是返回NULL。
6. strstr函数【在字符串中找一段字符】
函数原型:
1. char * strstr ( const char * str1, const char * str2);
作用:在一个字符串中查找另一个字符串的首次出现
使用规则:
- str1是被检索的字符串,str2是查找表字符串。(在str1中找str2)
- 在字符串str1中查找字符串str2的首次出现,并返回其地址。
- 如果没有,则返回NULL。
strstr的模拟实现:
char* my_strstr(const char* str1, const char* str2)
{
assert(str1 && str2);
char *pos = NULL, *start = str2;
//str2什么也没有
if (!*str2)
return pos;
//str2有字符串
while (*str1)
{
pos = str1;
str2 = start;
while (*str2 && *str1 && *str1++ == *str2++);
if (*str2 == '\0')
return pos;
str1 = pos+1;
}
return NULL;
}
解析:
(1)str1根据pos复位:str1在与str2匹配的时候,会往后走。如果匹配失败,则str1要回到pos的位置,并从pos+1的位置重新匹配;如果匹配成功,此时pos就是我们想要的位置。
(2)str2根据start复位:str1在与str2匹配的时候,str2也会往后走。当要重新匹配时,str2要要回到起始位置重新匹配。
2.2 其他字符串函数的 使用规则
7. strncpy函数
函数原型:
char* strncpy(char* destination, const char* source, size_t num);
使用规则:
- 保证destination足够长
- 复制字符串source中的前num个字符 (从序号0到序号num-1)
- 如果num大于source的字符串长度,那么多出几个字符,就补上几个\0,直到写入总数 num 个字符为止
8. strncat函数
函数原型:
char* strncat(char* destination, const char* source, size_t num);
使用规则:
- 保证destination足够长
- 将 source 的前 num 个字符拼接到 destination(序号0到num-1)
- 如果num大于source的长度,则source有多大就拼接多少,不会用\0补充。
9. strncmp函数
函数原型:
int strncmp(const char* str1, const char* str2, size_t num);
使用规则:
- 比较str1和str2的前num个字符。(返回值的规则与strcmp一样)
- 如果在第num-1个字符之前,已经首次出现了不同的字符,就结束比较并返回比较的结果。
- 如果str1与str2相等,且num比它们长,则不会再继续比较并返回0。(0表示两个字符串相等)
10. strrchr函数
函数原型:
char* strrchr(char* str, int character);
使用规则:
- 查找与character相同的字符
- 从右边开始,向左边查找。
- 找到右边首次出现的相同字符后,返回该处的地址(指针)
- 如果没有,则返回NULL
11. strtok函数
函数原型:
char * strtok ( char * str, const char * sep);
使用规则:
- 作用:按分隔符对字符串进行分割(每次只能分割一段)
- str是要被分割的字符串,sep是分隔符的字符集合。
- 第一次分割:此时strtok函数的参数str不为NULL ,函数将找到str中第⼀个标记,strtok函数将保存它在字符串中的位置。
- 每次分割完,该处的分隔符都会被制成\0来做成标记(改变了原字符串),然后返回⼀个指向这个标记的指针。
- 后面的分割:此时strtok函数的参数str为NULL ,函数将在同⼀个字符串中被保存的位置开始,查找下⼀个标记。
- 完全分割后:如果字符串中不存在更多的标记,则返回 NULL 指针。
注意:strtok函数会改变被操作的字符串,所以在使⽤strtok函数切分的字符串⼀般都是临时拷贝的内容并且可修改。
代码演示:
运行结果:
上面这个代码有2个语句重复出现了4次,我们可以用for循环来代替:这也是strtok的正确使用格式。
char* ps = NULL;
for (ps = strtok(str, sep); ps != NULL; ps = strtok(NULL, sep))
{
printf("%s\n", ps);
}
运行结果:
12. strerror函数 与 perror函数
函数原型: char * strerror ( int errnum );
作用:strerror函数可以把参数部分错误码对应的错误信息的字符串地址返回来。
包含头文件:<string.h>和<errno.h>
- 在不同的系统和C语⾔标准库的实现中都规定了⼀些错误码,⼀般是放在<errno.h>这个头⽂件中说明的
- C语⾔程序启动的时候就会使⽤⼀个全局变量errno来记录程序的当前错误码
- 只不过程序启动的时候errno是0,表示没有错误;当我们在使⽤标准库中的函数的时候发⽣了某种错误,就会讲对应的错误码,存放在errno中
- ⽽⼀个错误码的数字是整数很难理解是什么意思,所以每⼀个错误码都是 有对应的错误信息的。strerror函数就可以将错误对应的错误信息字符串的地址返回。
我们打印⼀下0~10这些错误码对应的信息:
#include<string.h>
#include<errno.h>
int main()
{
int i = 0;
for (i = 0; i <= 10; i++) {
printf("错误%d:%s\n", i, strerror(i));
}
return 0;
}
strerror函数多用于文件操作的打开检查
例如:
int main()
{
FILE* pFile;
pFile = fopen("123", "r");
if (pFile == NULL)
{
printf("错误原因: %s\n", strerror(errno));
}
return 0;
}
如果使用perror函数,可以缩减成这样:
int main()
{
FILE* pFile;
pFile = fopen("123", "r");
if (pFile == NULL)
{
perror("错误的原因:");
}
return 0;
}
perror函数使用前要包含头文件<errno.h>
perror的原型:
1. void perror(const char *s);
perror的作用:
- 直接打印错误信息
- 功能上,相当于是printf函数与strerror函数的结合。
本期分享结束,感谢大家的支持Thanks♪(・ω・)ノ