前言:在编程的过程中,我们经常要处理字符和字符串,为了⽅便操作字符和字符串,C语⾔标准库中提供了 ⼀系列库函数,接下来我们就学习⼀下这些函数。
目录
[1. 字符函数](#1. 字符函数)
[1.1 字符分类判断函数](#1.1 字符分类判断函数)
[1.2 字符转换函数](#1.2 字符转换函数)
[1.3 练习](#1.3 练习)
[2. 字符串函数](#2. 字符串函数)
[2.1 常用函数的 使用和模拟实现](#2.1 常用函数的 使用和模拟实现)
[1. strlen函数【计算字符串长度】](#1. strlen函数【计算字符串长度】)
[2. strcpy函数【拷贝字符串】](#2. strcpy函数【拷贝字符串】)
[3. strcat函数【拼接字符串】](#3. strcat函数【拼接字符串】)
[4. strcmp函数【比较两个字符串大小】](#4. strcmp函数【比较两个字符串大小】)
[5. strchr函数【在字符串中找一个字符】](#5. strchr函数【在字符串中找一个字符】)
[6. strstr函数【在字符串中找一段字符】](#6. strstr函数【在字符串中找一段字符】)
[2.2 其他字符串函数的 使用规则](#2.2 其他字符串函数的 使用规则)
[7. strncpy函数](#7. strncpy函数)
[8. strncat函数](#8. strncat函数)
[9. strncmp函数](#9. strncmp函数)
[10. strrchr函数](#10. strrchr函数)
[11. strtok函数](#11. strtok函数)
[12. strerror函数 与 perror函数](#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函数的模拟实现:
cpp
int my_toupper(int c)
{
if (c >= 'a' && c <= 'z')
{
c = c - 32;
}
return c;
}
1.3 练习
写⼀个代码,将字符串中的⼩写字⺟转⼤写,其他字符不变:
cpp
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函数【计算字符串长度】
函数原型:
- 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
cpp
strlen--"计算器累计"版
int my_strlen1(const char* str)
{
assert(str);
int count = 0;
char* p = str;
while (*p)
{
count++;
p++;
}
return count;
}
const修饰的原因:
保证了:不能通过解引用*str对原字符串进行修改
版本2
cpp
strlen--"指针-指针"版
int my_strlen2(const char* str)
{
assert(str);
char* p = str;
while (*p)
p++;
return p - str;
}
版本3
cpp
strlen--"函数递归"版
int my_strlen3(const char* str)
{
if (*str == '\0')
return 0;
else
return 1 + my_strlen3(str + 1);
}
函数递归也可以写成这样:
cpp
int my_strlen3(const char* str)
{
return *str=='\0' ? 0 : 1+my_strlen3(str+1);
}
2. strcpy函数【拷贝字符串】
函数原型:
- char* strcpy(char * destination, const char * source );
**作用:**把字符串source的内容复制到字符串destination中。
使用规则:
- source是源字符串,源字符串必须以 '\0' 结束。
- 会将源字符串中的 '\0' 也拷⻉到⽬标空间。
- ⽬标空间必须足够⼤,以确保能存放源字符串。
- ⽬标空间必须可修改。
strcpy的模拟实现:
cpp
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函数【拼接字符串】
函数原型:
- char* strcat(char * destination, const char * source );
**作用:**将一个字符串source追加到另一个字符串destination的末尾
使用规则:
- 源字符串必须以 '\0' 结束。
- 会将源字符串中的 '\0' 拷⻉到⽬标空间。
- ⽬标空间必须⾜够⼤,以确保能存放源字符串。
- ⽬标空间必须可修改。
strcat的模拟实现:
cpp
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函数【比较两个字符串大小】
函数原型:
- 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的模拟:
cpp
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);
while (*str1 && *str2 && *str1 == *str2 )
{
str1++;
str2++;
}
return *str1 - *str2;
}
其实也可以写成这样:
cpp
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函数【在字符串中找一个字符】
函数原型:
- char *strchr(const char *str, int c);
**作用:**在一个字符串中查找给定字符的第一个匹配之处
使用规则:
str
是要查找的字符串,c
是要查找的字符(输入时要acsii的值)- 从字符串的左边开始查找。
- 如果找到第一个与c对应的字符,则返回该字符的地址(指针)。
- 如果没有,则返回NULL。
strchr的模拟实现:
cpp
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函数【在字符串中找一段字符】
函数原型:
- char * strstr ( const char * str1, const char * str2);
**作用:**在一个字符串中查找另一个字符串的首次出现
使用规则:
- str1是被检索 的字符串,str2是查找表字符串。(在str1中找str2)
- 在字符串str1中查找字符串str2的首次出现,并返回其地址。
- 如果没有,则返回NULL。
strstr的模拟实现:
cpp
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的正确使用格式。
cpp
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这些错误码对应的信息:
cpp
#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函数多用于文件操作的打开检查
例如:
cpp
int main()
{
FILE* pFile;
pFile = fopen("123", "r");
if (pFile == NULL)
{
printf("错误原因: %s\n", strerror(errno));
}
return 0;
}
如果使用perror函数,可以缩减成这样:
cpp
int main()
{
FILE* pFile;
pFile = fopen("123", "r");
if (pFile == NULL)
{
perror("错误的原因:");
}
return 0;
}
perror函数使用前要包含头文件**<errno.h>**
perror的原型:
- void perror(const char *s);
perror的作用:
- 直接打印错误信息
- 功能上,相当于是printf函数与strerror函数的结合。
本期分享结束,感谢大家的支持Thanks♪(・ω・)ノ