🎈一、前言
在编程中,字符函数和字符串函数是非常重要的工具。它们可以用于处理字符串、操作字符和执行各种操作,如
转换大小写
、替换字符
、查找子字符串
等等。掌握这些函数可以帮助我们编写更高效、更易于维护的代码。C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在
常量字符串中
或者字符数组中
。
字符串常量
适用于那些对它不做修改的字符串函数
。
🎈二、字符函数
📌1. 字符分类函数:
函数 | 如果他的参数符合下列条件就返回真 |
iscntrl | 任何控制字符(根据标准 ASCII 字符集,控制字符的 ASCII 编码介于 0x00 (NUL) 和 0x1f (US) 之间,以及 0x7f (DEL),某些平台的特定编译器实现还可以在扩展字符集(0x7f 以上)中定义额外的控制字符。) |
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 | 任何可打印字符,包括图形字符和空白字符 |
ps: 以上字符分类函数的头文件都是 <ctype.h>
,且它们满足条件时返回的值都是 非0
的真值,不满足条件是返回的值都是 0
。
📌2. 字符转换:
【函数原型】:
int tolower ( int c );//转换成小写字母 int toupper ( int c );//转换成大写字母
【案例】:
// example #include <stdio.h> #include <ctype.h> int main() { int i = 0, j = 0; char str[] = "Test String.\n"; char c; while (str[i]) { c = str[i]; if (isupper(c)) c = tolower(c);//将大写字母转换成小写字母 putchar(c); i++; } while (str[j]) { c = str[j]; if (islower(c)) c = toupper(c);//将小写字母转换成大写字母 putchar(c); j++; } return 0; }
🎈三、长度不受限制的字符串函数(头文件都是 <string.h>
)
功能 | 函数 |
求字符串长度 | strlen |
长度不受限制的字符串函数 | strcpy、strcat、strcmp |
长度受限制的字符串函数介绍 | strncpy、strncat、strncmp |
字符串查找 | strstr、strtok |
错误信息报告 | strerror |
📌1. strlen函数
声明:
size_t strlen(const char *str);
参数:
str
- - - 要计算长度的字符串。返回值:
- 该函数返回字符串的长度。
- 字符串以
'\0'
作为结束标志,strlen
函数返回的是在字符串中'\0'
前面出现的字符个数(不包
含'\0'
)。 - 参数指向的字符串必须要以
'\0'
结束。 - 注意函数的返回值为
size_t
,是无符号的( 易错 )
【易错题】:
#include <stdio.h> 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 --> unsigned int
,使用本以为3-6
的结果为-3
,但是-3
被看成是一个无符号整型,则表示一个非常大的数。
🔎strlen 的模拟实现
【计数实现】
//计数实现 #include<stdio.h> #include<assert.h> size_t my_strlen(const char* str) { //下面对str进行了解引用操作,为了确保指针能够正常解引用,需要断言防止str为空指针 assert(str); size_t count = 0; while (*str++ != '\0') { count++; } return count; } int main() { char arr[] = "abcedf"; printf("字符串长度为:%zd\n", my_strlen(arr)); //结果是 6 return 0; }
【递归实现】
//递归实现 #include<stdio.h> #include<string.h> #include<assert.h> size_t my_strlen(const char* str) { assert(str); if (*str++) { return 1 + my_strlen(str); } else { return 0; } } int main() { char arr[] = "abcdef"; printf("字符串长度为:%zd\n", my_strlen(arr));//结果是 6 return 0; }
【指针运算实现】
#include<stdio.h> #include<assert.h> size_t my_strlen(char* str) { assert(str); char* tmp = str; while (*str) { str++; } return str - tmp; //指针 - 指针返回的是这两个指针之间的元素个数 } int main() { char arr[] = "abcedf"; printf("字符串长度为:%zd\n", my_strlen(arr));//结果是 6 return 0; }
📌2. stcpy函数
声明:
char* strcpy(char * dest, const char * src );
参数
dest
- - - 指向用于存储复制内容的目标数组。src
- - - 要复制的字符串。返回值
- 该函数返回一个指向最终的目标字符串
dest
的指针。
【易错点】:
- C 库函数
char *strcpy(char *dest, const char *src)
把src
所指向的字符串复制到dest
。需要注意的是如果目标数组dest
不够大,而源字符串的长度又太长,可能会造成缓冲溢出的情况。 - 源字符串必须以
'\0'
结束。否者,strcpy
无法判断在拷贝字符串时该拷贝到什么位置结束。 strcpy
会将源字符串中的'\0'
拷贝到目标空间。- 目标空间必须足够大,以确保能存放源字符串。
- 目标空间必须可变。
🔎strcpy 的模拟实现
//strcpy模拟实现 #include<stdio.h> #include<assert.h> char* my_strcpy(char* dest,const char* src) { assert(dest && src); //后面需要解引用指针,指针不能为空 char* ret = dest; //记录目标字符串的地址 while (*dest++ = *src++) // 当将'\0'拷贝到字符串中时,循环结束 { ; } return ret; } int main() { char arr1[20] = { 0 }; char arr2[] = "abcdef"; printf("%s\n", my_strcpy(arr1, arr2)); //结果是 abcdef return 0; }
📌3. strcat 函数
声明:
char * strcat ( char * dest, const char * src );
参数
dest
- - - 指向目标数组,该数组包含了一个 C 字符串,且足够容纳追加后的字符串。src
- - - 指向要追加的字符串,该字符串不会覆盖目标字符串。返回值
- 该函数返回一个指向最终的目标字符串
dest
的指针。
【注意事项】:
- C 库函数 char *strcat(char *dest, const char *src) 把 src 所指向的字符串追加到 dest 所指向的字符串的结尾(包括
'\0'
)。 - 源字符串必须以
'\0'
结束。 - 目标空间必须有足够的大,能容纳下源字符串的内容。
- 目标空间必须可修改。
- 不能实现自己给自己追加字符串的操作,会导致死循环。(原因是在追加时会破坏自己字符串里的内容,找不到追加结束标志 ‘\0’)
🔎strcat 的模拟实现
//strcat函数,返回的是目标空间的起始地址 char* my_strcat(char* dest, const char* src) { char* ret = dest; assert(dest && src); //1. 找到目标空间的末尾 while (*dest != '\0') { dest++; } //2. 数据追加 while (*dest++ = *src++) //追加到 '\0'跳出循环 { ; } return ret; } int main() { char arr1[20] = "abc"; char arr2[] = "def"; my_strcat(arr1, arr2); printf("%s\n", arr1); //结果是 abcdef return 0; }
📌4. strcmp 函数
声明:
int strcmp ( const char * str1, const char * str2 );
参数
str1
- - - 要进行比较的第一个字符串。str2
- - - 要进行比较的第二个字符串。返回值
该函数返回值如下:
- 如果返回值
小于 0
,则表示str1 小于 str2
。- 如果返回值
大于 0
,则表示str1 大于 str2
。- 如果返回值
等于 0
,则表示str1 等于 str2
。
【注意事项】:
- C 库函数
int strcmp(const char *str1, const char *str2)
把str1
所指向的字符串和str2
所指向的字符串进行比较。 - 标准规定:
第一个字符串大于
第二个字符串,则返回大于0
的数字
第一个字符串等于
第二个字符串,则返回0
第一个字符串小于
第二个字符串,则返回小于0
的数字
🔎strcmp 的模拟实现
// strcmp 的模拟实现 #include <stdio.h> #include <assert.h> int my_strcmp(const char* str1, const char* str2) { assert(str1 && str2); while (*str1 == *str2) { if (*str1 == '\0') //两个字符相等且等于'\0'时,说明两个字符串相等 return 0; str1++; str2++; } return *str1 - *str2; //此时两个字符不相等,*str1 > *str2返回大于0的数,*str1 < *str2,返回小于0的数 } int main() { char arr1[] = "abq"; char arr2[] = "abc"; if (my_strcmp(arr1, arr2) > 0) { printf(">\n"); } else { printf("<=\n"); } return 0; } // 结果是 >
🎈四、长度受限制的字符串函数(strncpy、strncat、strncmp)
头文件<string.h>
📌1. strncpy 函数
声明
char *strncpy(char *dest, const char *src, size_t n);
参数
dest
- - - 指向用于存储复制内容的目标数组。src
- - - 要复制的字符串。n
- - - 要从源中复制的字符数。返回值
- 该函数返回最终复制的字符串。
【注意事项】:
- C 库函数
char *strncpy(char *dest, const char *src, size_t n)
把src
所指向的字符串复制到dest
,最多复制n
个字符。当src
的长度小于n
时,dest
的剩余部分将用空字节填充。 - 拷贝
n
个字符从源字符串到目标空间。 - 如果源字符串的长度小于
n
,则拷贝完源字符串之后,在目标的后边追加0
,直到n
个。
【实例】:
#include <stdio.h> #include <string.h> int main() { char src[40]; char dest[12]; memset(dest, '\0', sizeof(dest)); strcpy(src, "This is runoob.com"); strncpy(dest, src, 10); printf("最终的目标字符串: %s\n", dest); return(0); }
📌2. strncat 函数
声明
char *strncat(char *dest, const char *src, size_t n);
参数
dest
- - - 指向目标数组,该数组包含了一个 C 字符串,且足够容纳追加后的字符串,包括额外的空字符。src
- - - 要追加的字符串。n
- - - 要追加的最大字符数。返回值
- 该函数返回一个指向最终的目标字符串 dest 的指针。
【注意事项】:
- C 库函数
char *strncat(char *dest, const char *src, size_t n)
把src
所指向的字符串追加到dest
所指向的字符串的结尾,直到n
字符长度为止。
【实例】:
#include <stdio.h> #include <string.h> int main () { char src[50], dest[50]; strcpy(src, "This is source"); strcpy(dest, "This is destination"); strncat(dest, src, 15); printf("最终的目标字符串: |%s|\n", dest); //结果是 |This is destinationThis is source| return(0); }
📌3. strncmp 函数
声明
int strncmp(const char *str1, const char *str2, size_t n);
参数
str1
- - - 要进行比较的第一个字符串。str2
- - - 要进行比较的第二个字符串。n
- - - 要比较的最大字符数。返回值
该函数返回值如下:
- 如果返回值
< 0
,则表示str1 小于 str2
。- 如果返回值
> 0
,则表示str1 大于 str2
。- 如果返回值
= 0
,则表示str1 等于 str2
。
【注意事项】:
- strncmp() 是一个标准库函数,用于比较两个字符串的
前 n 个字符
是否相等。 - strncmp() 函数通常用于比较两个字符串,以确定它们是否相等或哪个字符串在字典顺序上更小。
- C 库函数
int strncmp(const char *str1, const char *str2, size_t n)
把str1
和str2
进行比较,最多比较前n
个字符
【实例】:
/* strncmp example */ #include <stdio.h> #include <string.h> int main() { char str[][5] = { "R2D2" , "C3PO" , "R2A6" }; int n; puts("Looking for R2 astromech droids..."); for (n = 0; n < 3; n++) if (strncmp(str[n], "R2xx", 2) == 0) { printf("found %s\n", str[n]); } return 0; }
🎈五、特殊字符串函数
头文件<string.h>
📌1. strstr 函数
声明
char *strstr(const char *str1, const char *str2);
参数
str1
- - - 要被检索的 C 字符串。str2
- - - 在 haystack 字符串内要搜索的小字符串。返回值
- 该函数返回在
str1
中第一次出现str2
字符串的位置,如果未找到则返回null
。
【注意事项】:
- C 库函数
char *strstr(const char *str1, const char *str2)
在字符串str1
中查找第一次出现字符串str2
的位置,不包含终止符'\0'
。
🔎strstr 的模拟实现
//strstr 的模拟实现 #include<stdio.h> #include<assert.h> //BF字符串模式匹配算法 const char* my_strstr(const char* str1, const char* str2) { const char* cp;//记录开始匹配的位置 const char* s1;//遍历str1指向的字符串,为了不修改str1 const char* s2;//遍历str2指向的字符串,为了不修改str2 assert(str1 && str2); if (*str2 == '\0') return str1; cp = str1; //cp指向str1的起始位置 while (*cp) //str1不是空字符串,若是空字符串,则没有必要匹配 { s1 = cp; //回溯,s1从cp的位置开始匹配 s2 = str2;//回溯,从子串的起始位置开始 while (*s1 && *s2 && *s1 == *s2) //*s1或*s2指向'\0'时,就没有字符匹配了,跳出循环 { s1++; s2++; } if (*s2 == '\0') //如果*s2到了字符串末尾,则匹配成功 return cp; cp++; //从主串下一个位置开始匹配 } return NULL; } int main() { char arr1[] = "abbbcdef"; char arr2[] = "bbc"; const char* ret = my_strstr(arr1, arr2); if (ret == NULL) { printf("找不到\n"); } else { printf("%s\n", ret); } return 0; }
📌2. strtok 函数
声明
char *strtok(char *str, const char *sep);
参数
str
- - - 要被分解成一组小字符串的字符串。sep
- - - 包含分隔符的 C 字符串。返回值
- 该函数返回被分解的
第一个子字符串
,如果没有可检索的字符串,则返回一个空指针
。
【注意事项】:
- C 库函数
char *strtok(char *str, const char *sep)
分解字符串str
为一组字符串,sep
为分隔符。 - sep参数是个字符串,定义了用作分隔符的
字符集合
- 第一个参数指定一个字符串,它包含了
0
个或者多个由sep
字符串中一个或者多个分隔符分割的标
记。 strtok
函数找到str
中的下一个标记,并将其用'\0'
结尾,返回一个指向这个标记的指针。(注:strtok
函数会改变被操作的字符串,所以在使用strtok
函数切分的字符串一般都是临时拷贝的内容并且可修改。)strtok
函数的第一个参数不为NULL
,函数将找到str
中第一个标记,strtok
函数将保存它在字符串中的位置。strtok
函数的第一个参数为NULL
,函数将在同一个字符串中被保存的位置开始,查找下一个标记。- 如果字符串中不存在更多的标记,则返回
NULL
指针。
【实例】:
#include <string.h> #include <stdio.h> int main() { char str[80] = "This is - www.runoob.com - website"; char bufer[100]; strcpy(bufer, str); const char s[2] = "-"; char* token; // 获取第一个子字符串 token = strtok(bufer, s); // 继续获取其他的子字符串 while (token != NULL) { printf("%s\n", token); token = strtok(NULL, s); } return(0); }
📌3. strerror 函数
声明
char *strerror(int errnum);
参数
errnum
- - - 错误号,通常是errno
。返回值
- 该函数返回一个
指向错误字符串的指针
,该错误字符串描述了错误errnum
。
【拓展】:
- C 语言不提供对错误处理的直接支持,但是作为一种系统编程语言,它以返回值的形式允许您访问底层数据。在发生错误时,大多数的 C 或 UNIX 函数调用返回 1 或 NULL,同时会设置一个错误代码 errno,该错误代码是
全局变量
,表示在函数调用期间发生了错误。我们可以在errno.h
头文件中找到各种各样的错误代码。- 所以,C 程序员可以通过检查返回值,然后根据返回值决定采取哪种适当的动作。开发人员应该在程序初始化时,把 errno 设置为 0,这是一种良好的编程习惯。0 值表示程序中没有错误。
以下是C语言中 0~9
的错误码对应的错误信息:
【注意事项】:
- C 库函数
char *strerror(int errnum)
从内部数组中搜索错误号errnum
,并返回一个指向错误消息字符串的指针(这个字符串是对应的错误信息)。strerror
生成的错误字符串取决于开发平台和编译器
。
【实例】:
//打开文件的例子 //fopen 以读的形式打开文件 // 如果文件存在,打开成功 // 如果文件不存在,打开失败 #include <stdio.h> #include <string.h> #include <errno.h> //必须包含的头文件 int main() { FILE* fp; fp = fopen("file.txt", "r"); if (fp == NULL) { //C语言使用库函数时,如果发生错误,就会将相应的错误码放在errno这个全局变量中 printf("Error: %s\n", strerror(errno)); //errno: Last error number } return 0; }
📌3.perror 函数 (printf + strerror)
声明
void perror(const char *str);
参数
str
- - - 这是 C 字符串,包含了一个自定义消息,将显示在原本的错误消息之前
。返回值
- 该函数不返回任何值。
【注意事项】:
- C 库函数
void perror(const char *str)
把一个描述性错误消息输出到标准错误stderr
。首先输出字符串str
,后跟一个冒号,然后是一个空格。 - 可以把
perror
函数的功能理解为printf + strerror
【实例】:
//打开文件的例子 //fopen 以读的形式打开文件 // 如果文件存在,打开成功 // 如果文件不存在,打开失败 #include <stdio.h> int main() { FILE* fp; // 首先重命名文件 rename("file.txt", "newfile.txt"); // 现在让我们尝试打开相同的文件 fp = fopen("file.txt", "r"); if (fp == NULL) { perror("Error: "); return -1; //return 非0,表示异常退出 } fclose(fp); //关闭文件 return 0; //表示正常退出 }
今天的内容到此就结束了,其中有很多细节的知识点需要大家细细品味,祝大家在成为大佬的路上越走越远,成为一名优秀的程序猿 😎