在编程的过程中,我们经常需要处理字符和字符串,为了方便操作字符和字符串,C语言标准库中提供了一系列的库函数,接下来我们就学习一下这些函数。
1 字符分类函数
C语言中有一系列的函数是专门做字符分类的,也就是一个字符属于什么类型的字符。
这些函数的使用都需要包含一个头文件 ctype.h 。
| 函数 | 如果他的参数符合下列条件就返回真 |
|---|---|
| iscntrl | 任何控制字符 |
| isspace | 空白字符:空格' ',换页'\f',换行'\n',回车'\r',制表符'\t'或者垂直制表符'\v' |
| isdigit | 十进制数字'0'~'9'字符 |
| isxdigit | 十六进制数字,包括所有十进制数字字符,小写字母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 | 任何可打印字符,包括图形字符和空白字符 |
这些函数的使用方法非常相似,我们就介绍一个函数的使用,其他的非常相似:
cpp
int islower( int c );
islower 是能够判断参数部分的 c 是否是小写字母的。
通过返回值来说明是否是小写字母,如果是小写字母就返回非0的整数,如果不是小写字母,则返回0。
**练习:**写一个代码,将字符串中的小写字母转大写,其他字符不变。
cpp
#include<stdio.h>
#include<ctype.h>
int main()
{
char arr[] = "My Name Is piku.";
int i = 0;
while (arr[i])
{
if (islower(arr[i]))
{
arr[i] = arr[i] - 32;
}
putchar(arr[i]); //putchar - 将字符写入标准输出
i++;
}
printf("\n");
return 0;
}
输出结果:

2 字符转换函数
C语言提供了2个字符转换函数:
cpp
int tolower( int c ); //将参数传进去的大写字母转小写
int toupper( int c ); //将参数传进去的小写字母转大写
上面的代码,我们将小写转大写,是 -32 完成的效果,有了转换函数,就可以直接使用**tolower** 函数。
cpp
#include<stdio.h>
#include<ctype.h>
int main()
{
char arr[] = "My Name Is piku.";
int i = 0;
while (arr[i])
{
if (islower(arr[i]))
{
//arr[i] = arr[i] - 32;
toupper(arr[i]);
}
putchar(arr[i]); //putchar - 将字符写入标准输出
i++;
}
printf("\n");
return 0;
}
输出结果:

3 strlen的使用和模拟实现
cpp
size_t strlen ( const char* str );
- 字符串以**'\0'** 作为结束标志,strlen函数返回的是在字符串中**'\0'** 前面出现的字符个数(不包含 '\0')。
- 参数指向的字符串必须要以 '\0' 结束。
- 注意函数的返回值为size_t 类型,是无符号的(易错)。
- strlen的使用需要包含头文件string.h。
那我们看看下面的代码输出什么呢?
cpp
#include <stdio.h>
#include <string.h>
int main()
{
const char* str1 = "abc";
const char* str2 = "abcdef";
if(strlen(str1) - strlen(str2) > 0)
{
printf("str1 大于 str2\n");
}
else
{
printf("str1 小于等于 str2\n");
}
return 0;
}
输出结果为:

strlen(str1) 的结果为3,strlen(str2) 的结果为6,那么 3-6=-3,-3应该是小于0,然后打印str1 小于等于 str2 的才对,那么问题出在哪呢?答案是strlen的返回值类型是size_t类型的,是无符号的,所以3-6计算出的-3,会被当作是无符号数,那么它就是一个很大的正数了,所以会打印大于。
想要达到我们原先想要的结果可以如下改写:
cpp
#include <stdio.h>
#include <string.h>
int main()
{
const char* str1 = "abc";
const char* str2 = "abcdef";
if ((int)strlen(str1) - (int)strlen(str2) > 0) //强制类型转换为有符号整型即可
{
printf("str1 大于 str2\n");
}
else
{
printf("str1 小于等于 str2\n");
}
return 0;
}
输出结果为:

strlen模拟实现:
方式1:计数器方式
cpp#include <assert.h> size_t my_strlen(const char* str) { size_t count = 0; assert(str != NULL); //确保我们访问str的时候不是空指针 while(*str != '\0') { count++; str++; } return count; }
方式2:指针-指针方式
cpp#include <assert.h> size_t my_strlen(const char* str) { const char* start = str; assert(str != NULL); //确保我们访问str的时候不是空指针 while (*str) //'\0'的ASCII码值是0 { str++; } return str - start; }
方式3:不创建临时变量 - 递归
cpp//计算的过程可以拆解成下面步骤 my_strlen("abcdef") ->1 + mystrlen("bcdef") ->1 + 1 + mystrlen("cdef") ->1 + 1 + 1 + mystrlen("def") ->1 + 1 + 1 + 1 + mystrlen("ef") ->1 + 1 + 1 + 1 + 1 + mystrlen("f") ->1 + 1 + 1 + 1 + 1 + 1 + mystrlen("") ->1 + 1 + 1 + 1 + 1 + 1 + 0
cpp#include <assert.h> size_t my_strlen(const char* str) { assert(str); if (*str == '\0') return 0; else return 1 + my_strlen(str + 1); }
4 strcpy的使用和模拟实现
cpp
char* strcpy(char* destination, const char* source);
- Copies the C string pointed by source into the array pointed by destination, including the terminating null character (and stopping at that point).
- 源字符串必须以**'\0'**结束。
- 会将源字符串中的**'\0'**拷贝到目标空间。
- 目标空间必须足够大,以确保能存放源字符串。
- 目标空间必须可修改。
- strcpy的使用需要包含头文件string.h。
- srtcpy返回的值是目标空间的起始地址。
cpp
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[20] = { 0 };
char arr2[] = "PIKU";
strcpy(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
输出结果为:

strcpy模拟实现:
基础功能实现:
cpp#include <assert.h> void my_strcpy(char* dest, const char* src) { while (*src) { *dest = *src; dest++; src++; } *dest = *src; }
符合原函数并且更巧妙的实现:
cpp#include <assert.h> char* my_strcpy(char* dest, const char* src) { assert(dest); char* ret = dest; assert(src); //'\0'赋值之后,整个表达式结果就是'\0',ASCII码值是0,表达式为假,就跳出循环 //后置++是先使用再+1,也就是先对指针解引用,找到指针所指向的值,然后再对这个指针+1 while (*dest++ = *src++) { ; } return ret; }
5 strcat的使用和模拟实现
cpp
char* strcat ( char* destination, const char* source );
- Appends a copy of the source string to the destination string. The terminating null character in destination is overwritten by the first character of source , and a null-character is included at the end of the new string formed by the concatenation of both in destination.
- 源字符串必须以 '\0' 结束。
- 目标字符串中也得有 \0 ,否则没办法知道追加从哪里开始。
- 目标空间必须有足够的大,能容纳下源字符串的内容。
- 目标内容必须可修改。
- 字符串自己给自己追加是有风险的,不建议这样使用。
- strcat的使用需要包含头文件string.h。
cpp
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[20] = "hello ";
char arr2[] = "PIKU";
strcat(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
上面程序的运行结果是什么?

strcat模拟实现:
cpp
#include<assert.h>
char* my_strcat(char* dest, const char* src)
{
assert(dest && src);
char* ret = dest;
//1 找到目标空间的\0
while (*dest)
{
dest++;
}
//2 拷贝
while (*dest++ = *src++)
{
;
}
return ret;
}
6 strcmp的使用和模拟实现
cpp
int strcmp ( const char* str1, const char* str2 );
- This function starts comparing the first character of each string. If they are equal to each other, it continues with the following pairs until the characters differ or until a terminating null-character is reached.
- 标准规定:
① 第一个字符串大于第二个字符串,则返回大于0的数字
② 第一个字符串等于第二个字符串,则返回0
③ 第一个字符串小于第二个字符串,则返回小于0的数字
④那么如何判断两个字符串?比较两个字符串中对应位置上字符ASCII码值的大小。 - strcmp的使用需要包含头文件string.h。

strcmp模拟实现:
方式1:仅返回0,1,-1
cppint my_strcmp(const char* s1, const char* s2) { assert(s1 && s2); while (*s1 == *s2) { if (*s1 == '\0') return 0; s1++; s2++; } if (*s1 > *s2) return 1; else return -1; }
方式2:返回0,大于零的数,小于零的数
cppint my_strcmp(const char* s1, const char* s2) { assert(s1 && s2); while (*s1 == *s2) { if (*s1 == '\0') return 0; s1++; s2++; } return *s1 - *s2; }
7 strncpy函数的使用
cpp
char* strncpy ( char* destination, const char* source, size_t num );
- Copies the first num characters of source to destination . If the end of the source C string (which is signaled by a null-character) is found before num characters have been copied, destination is padded with zeros until a total of num characters have been written to it.
- 拷贝num个字符从源字符串到目标空间。
- 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。
- strmcpy的使用需要包含头文件string.h。


8 strncat函数的使用
cpp
char* strncat ( char* destination, const char* source, size_t num );
-
Appends the first num characters of source to destination , plus a terminating null-character.(将source指向字符串的前num个字符追加到destination指向的字符串末尾,再追加一个**\0** 字符)
-
If the length of the C string in source is less than num, only the content up to the terminating null-character is copied.(如果source指向的字符串的长度小于num的时候,只会将字符串中到 \0 的内容追加到destination指向的字符串末尾)
-
strncat的使用需要包含头文件string.h。



9 strncmp函数的使用
cpp
int strncmp ( const char* str1, const char* str2, size_t num );
- Compares up to num characters of the C string str1 to those of the C string str2.
- This function starts comparing the first character of each string. If they are equal to each other, it continues with the following pairs until the characters differ, until a terminating null-character is reached, or until num characters match in both strings, whichever happens first.
- 比较str1和str2的前num个字符,如果相等就继续往后比较,最多比较num个字母,如果提前发现不一样,就提前结束,大的字符所在的字符串大于另外一个。如果num个字符都相等,就是相等返回0。
- strncmp的使用需要包含头文件string.h。


10 strstr的使用和模拟实现
cpp
char* strstr ( const char* str1, const char* str2 );
- Returns a pointer to the first occurrence of str2 in str1 , or a null pointer if str2 is not part of str1 .(函数返回字符串str2在字符串str1中第一次出现的位置)。
- The matching process does not include the terminating null-characters, but it stops there.(字符串的比较匹配不包含 \0 字符,以 \0 作为结束标志)。
- strstr的使用需要包含头文件string.h。


strstr模拟实现:
cpp
#include<assert.h>
//暴力求解
char* my_strstr(const char* str1, const char* str2)
{
const char* cur = str1;
const char* s1 = NULL;
const char* s2 = NULL;
assert(str1 && str2);
if (*str2 == '\0') //处理特殊情况,str2是空指针的话就不用找了
{
return (char*)str1;
}
while (*cur)
{
s1 = cur;
s2 = str2;
//1.s1找到了\0,*s2 == '\0' - 找到了
//2.s2找到了\0 - 找到了
//3.*s1 != *s2
while (*s1 == *s2)
{
s1++;
s2++;
}
if (*s2 == '\0') //如果跳出循环之后,s2指向的是'\0',就说明找到了
{
return (char*)cur;
}
cur++; //没找到就继续往后找,新的起始位置
}
return NULL;
}
11 strtok函数的使用
cpp
char* strtok ( char* str, const char* sep );
- sep参数指向一个字符串,定义了用作分隔符的字符集合。
- 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
- strtok函数找到str中的下一个标记,并将其用**\0**结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
- strtok函数的第一个参数不为 NULL ,函数将找到str中的第一个标记,strtok函数将保存它在字符串中的位置。
- strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
- 如果字符串中不存在更多的标记,则返回 NULL 指针。
cpp
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[] = "PIKU666@163.com";
char arr2[30] = { 0 };
strcpy(arr2, arr1);
const char* p = "@.";
char* s = strtok(arr2, p);
printf("%s\n", s);
s = strtok(NULL, p);
printf("%s\n", s);
s = strtok(NULL, p);
printf("%s\n", s);
return 0;
}
上面程序运行结果为:

一般我们在使用的时候是不知道一个字符串中有几个sep字符串的几个分隔符的,我们采用如下方式实现:
cpp
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[] = "PIKU666@163.com#3648&SHUBAO";
char arr2[30] = { 0 };
strcpy(arr2, arr1);
const char* p = "@.#&";
char* s = NULL;
for (s = strtok(arr2, p); s != NULL; s = strtok(NULL, p))
{
printf("%s\n", s);
}
return 0;
}
运行结果如下:

12 strerror函数的使用
cpp
char* strerror ( int errnum );
- strerror函数可以把参数部分错误码对应的错误信息的字符串的地址返回来。
- 在不同的系统和C语言标准库的实现中都规定了一些错误码,一般是放在 errno.h 这个头文件中说明的,C语言程序启动的时候就会使用一个全局变量errno来记录程序的当前错误码,只不过程序启动的时候errno是0,表示没有错误,当我们在使用标准库中的函数的时候发生了某种错误,就会将对应的错误,存放在errno中,而一个错误码的数字是整数很难理解是什么意思,所以每一个错误码都是有对应的错误信息的。strerror函数就可以将错误对应的错误信息字符串的地址返回。
cpp
#include<stdio.h>
#include<string.h>
#include<errno.h>
int main()
{
//我们打印一下0~10这些错误码对应的信息
int i = 0;
for (i = 0; i <= 10; i++)
{
printf("%d : %s\n", i, strerror(i));
}
return 0;
}
在Windows+VS2022环境下输出的结果如下:

cpp
0 : No error
1 : Operation not permitted
2 : No such file or directory
3 : No such process
4 : Interrupted function call
5 : Input/output error
6 : No such device or address
7 : Arg list too long
8 : Exec format error
9 : Bad file descriptor
10 : No child processes
举例:
cpp
#include<stdio.h>
#include<string.h>
#include<errno.h>
int main()
{
FILE* pFlie;
pFlie = fopen("study.txt", "r");
if (pFlie == NULL)
printf("Errot opening flie study.txt: %s\n", strerror(errno)); //第十行
else
printf("文件打开成功\n");
return 0;
}
输出:
cpp
Errot opening flie study.txt: No such file or directory
也可以了解一下perror函数,perror函数相当于一次将上述代码中的第10行完成了,直接将错误信息打印出来。perror函数打印完参数部分的字符串之后,再打印一个冒号和空格,再打印错误信息。
cpp
#include<stdio.h>
#include<string.h>
#include<errno.h>
int main()
{
FILE* pFlie;
pFlie = fopen("study.txt", "r");
if (pFlie == NULL)
perror("Errot opening flie study.txt");
else
printf("文件打开成功\n");
return 0;
}
输出:
cpp
Errot opening flie study.txt: No such file or directory
