C语言初阶——字符及字符串函数详解
- 字符分类函数
C语言中有一系列的函数是用来给字符分类的,也就是判断一个字符是属于什么类型的字符。 这些函数的使用都需要包含一个头文件 <ctype.h>。
函数 | 返回值为真满足的条件 |
---|---|
iscntrl | 任何控制字符 |
isspace | 空白字符,例如空格’ ‘,换行’\n’,制表符’\t’等 |
isdigit | 十进制数字 0~9 |
isxdigit | 十六进制数字 0~9,a~f,A~F |
islower | 小写字母 a~z |
isupper | 大写字母 A~Z |
isalpha | 字母 A~Z 和 a~z |
isalnum | 字母和数字,包括字母 A~Z , a~z和十进制数字 0~9 |
ispunct | 标点符号,任何不属于数字和字母的图形字符(可打印) |
isgraph | 任何图形字符 |
isprint | 任何可打印字符,包括图形字符和空白字符 |
- 函数的声明
这些函数的使用方法非常类似,我们就以⼀个函数 islower( ) 为例:
int islower ( int c );
该函数接受的值为一个int类型的变量(对应字符的ASCII码),若满足条件,返回一个非0的整数,否则返回整数0.
#include<stdio.h>
#include<ctype.h>
int main()
{
printf("A: %d\n", islower('A'));
printf("a: %d\n", islower('a'));
return 0;
}
输出结果:
A: 0
a: 2
- 练习:写一串代码,将字符串中的小写字母转化为大写字母,其他字符保持不变。
#include<stdio.h>
#include<ctype.h>
//练习:写一串代码,将字符串中的小写字母转化为大写字母,其他字符保持不变。
int main()
{
char str[] = "hEllo wOrLd!";
char* ps = str;
while (*ps != '\0') {
//等于0是大写,不等于0为小写
if (islower(*ps)) {
//小写转化为大写为减去('a'-'A')
*ps -= ('a' - 'A');
}
ps++;
}
printf("%s", str);
return 0;
}
输出结果:HELLO WORLD!
- 字符转换函数
C语言提供了2个字符转换函数:
int tolower(int c);//将传入的⼤写字⺟转化为⼩写字母
int toupper(int c);//将传入的小写字⺟转化为大写字母
传入的值为字符的ASCII码值,输出的值也为转化后字符对应的ASCII码值。注:依然需要调用头文件 <ctype.h> 来使用。
通过此函数,我们可以将上述案例改写为:
#include<stdio.h>
#include<ctype.h>
//练习:写一串代码,将字符串中的小写字母转化为大写字母,其他字符保持不变。
int main()
{
char str[] = "hEllo wOrLd!";
char* ps = str;
while (*ps != '\0') {
//等于0是大写,不等于0为小写
if (islower(*ps)) {
*ps = toupper(*ps);
}
ps++;
}
printf("%s", str);
return 0;
}
补充说明:
- 字符的输入与输出函数——putchar( ) 函数与getchar( ) 函数
#include<stdio.h>
int main()
{
char ch = getchar();
putchar(ch);
return 0;
}
输入:char 输出:c
- 字符串的输入与输出函数——gets( ) 函数与puts( ) 函数
注意:由于gets( ) 函数不安全,在VS2022上无法演示,这里改用gets_s( ) 函数。
#include<stdio.h>
int main()
{
char str[100] = { 0 };
gets_s(str, 12);
puts(str);
return 0;
}
输入:hello world
输出:hello world
- strlen( )函数及其模拟实现
- 函数声明
size_t strlen ( const char* str );
需要注意的是:
strlen( )函数的返回值为 size_t,是无符号类型
strlen( )函数的使用需要包含头文件 <string.h>
易错点演示:
#include<stdio.h>
#include<string.h>
int main()
{
char str1[] = "abc";
char str2[] = "abcdefg";
if (strlen(str1) - strlen(str2) > 0)
printf("str1 > str2");
else
printf("str1 < str2");
return 0;
}
输出结果:str1 > str2
通过分析可知,str1 小于 str2,两字符串长度只差应该是小于0的数字,但是由于函数 strlen( ) 的输出结果的类型为size_t,差的结果只能为正数,所以无法显示正确结果。
- strlen( )函数的模拟实现
使用计数器模拟实现:
size_t my_strlen(const char* str)
{
int cnt = 0;
while (*str) {
cnt++;
str++;
}
return cnt;
}
int main()
{
char str[] = "abcdefg";
printf("%zd ", my_strlen(str));
return 0;
}
不创建临时变量模拟实现(函数递归思想):
size_t my_strlen(const char* str)
{
if (*str == '\0')
return 0;
else
return my_strlen(str + 1) + 1;
}
int main()
{
char str[] = "abcdefg";
printf("%zd ", my_strlen(str));
return 0;
}
使用指针之差模拟实现:
size_t my_strlen(const char* str)
{
char* ps = str;
while (*ps != '\0') {
ps++;
}
return ps - str;
}
int main()
{
char str[] = "abcdefg";
printf("%zd ", my_strlen(str));
return 0;
}
- strcyp( )函数及其模拟实现
- 函数声明
char* strcpy(char* destination, const char* source);
//前面的参数为需要拷贝的数组,后面的参数为提供字符串的数组
- 注意事项:
源字符串必须以 ‘\0’ 结尾
该函数会将源字符串中的 ‘\0’ 拷贝到目标空间
目标空间必须足够大,以确保能存放源字符串
目标空间必须为可修改的类型
- 使用案例:
#include<stdio.h>
#include<string.h>
int main()
{
char str1[] = "abcdefg";
char str2[10] = { 0 };
strcpy(str2, str1);
printf("str1: %s\n",str1);
printf("str2: %s\n",str2);
return 0;
}
输出结果:
str1: abcdefg
str2: abcdefg
- strcpy( )函数的模拟实现
方法一:
#include<stdio.h>
char* my_strcpy(char* dest, const char* sour)
{
char* ret = dest;
while (*sour != '\0') {
*dest = *sour;
dest++;
sour++;
}
return ret;
}
int main()
{
char str1[] = "abcdefg";
char str2[10] = { 0 };
my_strcpy(str2, str1);
printf("str1: %s\n",str1);
printf("str2: %s\n",str2);
return 0;
}
注:此方法无法将sour数组结尾处的 ‘\0’ 拷贝至目标数组,所以并不是最优解。
方法二:
char* my_strcpy(char* dest, const char* sour)
{
char* ret = dest;//注意这里是把dest的地址放到了ret中,以下语句均是通过指针来修改对应的地址
//后置++,先比较,再自增
while (*dest++ = *sour++) {
;
}
return ret;
}
int main()
{
char str1[] = "abcdefg";
char str2[10] = { 0 };
my_strcpy(str2, str1);
printf("str1: %s\n",str1);
printf("str2: %s\n",str2);
return 0;
}
解析:自增运算符的优先级高于解引用运算符的优先级,所以先实现++,后实现解引用后的比较。当sour自增到 ‘\0’ 时,将 ‘\0’ 赋值给dest后,整体表达式为假,跳出循环。
补充说明:
int main()
{
int a = 100, b = 10, c = 50;
a = (b = c);//与上述while循环内部的语句等价
printf("a=%d", a);
return 0;
}
执行结果:a = 50
- strcat( )的使用和模拟实现
- 注意事项:
源字符串必须以 ‘\0’ 结尾
目标字符串中也得有 ‘\0’ ,否则无法得知从哪里开始追加(追加时会覆盖被追加字符串的 ‘\0’ )
目标空间必须有足够大的空间,能容纳下源字符串的内容
目标空间必须是可修改的类型
- 函数声明
char* strcat(char* destination, const char* source);
//将source的内容追加至destination
- 使用案例
#include<stdio.h>
#include<string.h>
int main()
{
char str1[20] = "abcdefg";
char str2[20] = "hijklmn";
strcat(str1, str2);
printf("%s\n", str1);
return 0;
}
输出结果:abcdefghijklmn
- strcat( )函数的模拟实现
#include<stdio.h>
char* my_strcat(char* dest, const char* sour)
{
char* ret = dest;
//先找到被追加字符串的\0位置
while (*dest != '\0') {
dest++;
}
//找到\0了,将sour字符串的内容赋值给dest即可
while (*dest++ = *sour++) {
;
}
return ret;
}
int main()
{
char str1[20] = "abcdefg";
char str2[20] = "hijklmn";
my_strcat(str1, str2);
printf("%s\n", str1);
return 0;
}
- strcmp( )函数及其模拟实现
- 标准规定:
第⼀个字符串大于第二个字符串,则返回大于0的数字
第⼀个字符串等于第二个字符串,则返回数字0
第⼀个字符串小于第二个字符串,则返回小于0的数字
- 比较方法:
比较对应位置字符的ASCII码值,其本质与字符长度无关。
例如:
char str1[] = { 'a','b', 'c', 'd', 'e', 'f', '\0' };//小
char str1[] = { 'a','b', 'd', 'e', 'f', '\0' };//大
//其中a和b的对应位置的字符串相等,但是c<d,发现不相等的字符后就会停止比较,所以str1<str2。
- 函数声明
int strcmp(const char* str1, const char* str2);
- 使用案例:
#include<stdio.h>
#include<string.h>
int main()
{
char str1[] = "abcdefg";
char str2[] = "abcdf";
int ret = strcmp(str1, str2);
printf("%d\n", ret);
return 0;
}
输出结果:-1
- strcmp( )函数的模拟实现
#include<stdio.h>
int my_strcmp(const char* str1, const char* str2)
{
while (*str1 == *str2) {
if (*str1 == '\0') {
return 0;
}
str1++;
str2++;
}
int ret = *str1 - *str2;
if (ret > 0)
return 1;
else
return -1;
}
int main()
{
char str1[] = "abcd";
char str2[] = "abcde";
int ret = my_strcmp(str1, str2);
printf("%d\n", ret);
return 0;
}
输出结果:-1
- strncyp( )函数,strncat( )函数和strcmp( )函数的使用
- strncpy( )函数
拷贝num个字符从源字符串到目标空间
如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加 ‘\0’,直到num个
char* strncpy(char* destination, const char* source, size_t num);
- strcat( )函数
将source指向字符串的前num个字符追加到destination指向的字符串末尾,再追加一个 ‘\0’ 字符
若source指向的字符串的长度小于num,只会将字符串中到 ‘\0’ 的内容追加到destination指向的字符串末尾(不会补充 ‘\0’ 字符)
使用案例:
#include<stdio.h>
#include<string.h>
int main()
{
char str1[100] = "ABCDE";
char str2[100] = "123456789AB";
strncat(str1, str2, 5);
printf("%s\n", str1);
}
输出结果:ABCDE12345
- strncmp( )函数
比较str1和str2的前num个字符,如果相等就继续往后比较,最多比较num个字符
如果再比较num个字符前发现不同,则提前结束,返回对应的值,如果num个字符都相等,则返回数字0
- strstr( )函数的使用及其模拟实现
- 函数声明
char* strstr(const char* str, const char* substr);
注意事项:
函数返回字符串substr在字符串str中第一次出现的位置
字符串的匹配不包含 ‘\0’ 字符,以 ‘\0’ 字符作为结束标志
若未找到对应的位置,则返回空指针NULL
- 使用案例:
#include<stdio.h>
#include<string.h>
int main()
{
char str1[] = "This is a symple string";
char* ps = "symple";
char* ret = strstr(str1, ps);
printf("%s\n", ret);
return 0;
}
输出结果:symple string
#include<stdio.h>
#include<string.h>
int main()
{
char str1[] = "This is a symple string";
char* ps = "apple";
char* ret = strstr(str1, ps);
printf("%s\n", ret);
return 0;
}
输出结果:(null)
- strstr( )函数的模拟实现
#include<stdio.h>
char* my_strstr(const char* str1, const char* str2)
{
char* cp = (char*)str1;
char* s1, * s2;
if (!*str2)
return((char*)str1);
while (*cp)
{
s1 = cp;
s2 = (char*)str2;
while (*s1 && *s2 && !(*s1 - *s2))
s1++, s2++;
if (!*s2)
return(cp);
cp++;
}
return(NULL);
}
int main()
{
char str1[] = "This is a symple string";
char* ps = "symple";
char* ret = my_strstr(str1, ps);
printf("%s\n", ret);
return 0;
}
- strtok( )函数的使用
- 函数声明
char* strtok(char* str, const char* sep);
- 注意事项
sep指向⼀个字符串,定义了用于分隔字符串str的字符集合
str指定⼀个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记
strtok( ) 函数找到str中的下一个标记,并将其用字符 ‘\0’ 结尾,返回一个指向这个标记的指针
注: strtok( ) 函数会改变被操作的字符串,所以在使用该函数切分的字符串时一般都是临时拷贝的内容并且为可修改的类型
strtok( )函数的第一个参数不为NULL,函数将找到str中第⼀个标记,该函数将保存它在字符串中的位置
strtok( )函数的第一个参数为NULL,函数将在同⼀个字符串中被保存的位置开始,查找下一个标记
如果字符串中不存在更多的标记,则返回NULL指针
- 使用案例
#include<stdio.h>
#include<string.h>
int main()
{
char arr[] = "192.168.698.111.193.574";
char* sep = ".";
char* str = NULL;
//该函数第一次分割时传入数组arr,之后由于该函数记得位置,传入空指针即可
//当在目标字符串中无法发现分隔符时,则返回空指针,跳出循环
for (str = strtok(arr, sep); str != NULL; str = strtok(NULL, sep))
{
printf("%s\n", str);
}
return 0;
}
- strerror( ) 函数的使用
- 函数声明
char* strerror(int errnum);
strerror( ) 函数可以把参数部分错误码对应的错误信息的字符串地址返回来
错误码放置于 <errno.h> 这个头文件中,0表示没有错误,我们通过strerror( )函数得到错误码对应的字符串,从而获取里面的信息
errnum 为调用了头文件 <errno.h> 后,若产生错误,则将错误码自动放入errnum中
- 使用案例:
#include<stdio.h>
#include<string.h>
int main()
{
for (int i = 0; i <= 5; i++) {
printf("%s\n", strerror(i));
}
return 0;
}
输出结果:
No error
Operation not permitted
No such file or directory
No such process
Interrupted function call
Input/output error
- 使用案例:
#include<stdio.h>
#include<string.h>
#include<errno.h>
int main()
{
FILE* pFile;
pFile = fopen("text,exe", "r");
if (pFile == NULL)
printf("Error: %s\n", strerror(errno));//errno就是错误码存储的变量,需要调用头文件<errno.h>使用
return 0;
}
输出结果:Error: No such file or directory
- perror( ) 函数
#include<stdio.h>
#include<string.h>
#include<errno.h>
int main()
{
FILE* pFile;
pFile = fopen("text.exe", "r");
if (pFile == NULL)
perror("Error");
return 0;
}
注意:函数perror( )就是打印错误信息,里面需要传递一个字符串,在打印过程中,该字符串会显示在错误信息前面,若传入空字符串则只显示错误信息
输出结果:Error: No such file or directory