C语言:字符函数和字符串函数—及模拟实现
目录
1. 字符分类函数
2. 字符转换函数
3. 字符串相关函数
3.1 strlen
3.2 strcpy 和 strncpy
3.2.1 strcpy
3.2.2 strncpy
3.3 strcat 和 strncat
3.3.1 strcat
3.3.2 strncat
3.4 strcmp 和 strncmp
3.4.1 strcmp
3.4.2 strncmp
3.5 strstr
3.6 strtok
3.7 strerror
1. 字符分类函数
顾名思义,字符分类函数就是用于分类各种类型的字符,以便于后续操作。以下是一些常见的字符分类函数:
- iscntrl(控制字符)
- isspace(空白字符,如:空格 ' ' ,换行 '\n',制表符 '\t',换页 '\f',回车 '\r',垂直制表符 '\v')
- isdigit(十进制数)
- isxdigit(十六进制数)
- isalpha(大、小写字母)
- islower(小写字母)
- isupper(大写字母)
- isalnum(字母或数字)
- ispunct(标点符号,任何不属于字母或数字的图形字符)
- isgraph(任何图形字符)
- isprint(任何可打印字符,包括图形字符和空白字符)
2. 字符转换函数
在C语言中,除了将小写字母的ASCII码值减去32得到对应大写字母的ASCII码值,我们还可以用以下两个函数实现大小写转换:
int tolower(int a); //大写转小写 int toupper(int b); //小写转大写需要注意的是,函数的返回值为对应字母的ASCII码值。
3. 字符串相关函数
3.1 strlen
使用前须添加头文件<string.h>
size_t strlen (const char * str);
- 用法:统计字符串中 '\0' 之前的字符个数。
- 参数:str为需要统计长度的字符串指针。
- 返回值:返回str指向的字符串长度,为无符号整数(size_t)。
注意事项:
- 字符串必须有结束标志 '\0' 。
- strlen统计的是 '\0' 之前的字符个数,包括任何有效字符(如空格),不包括'\0' 。
- 返回值size_t类型是无符号的。
使用示例:
#include<stdio.h> #include<string.h> int main() { char arr[20] = {"hello world"}; int sz = strlen(arr); printf("%d", sz); return 0; }* 模拟实现strlen:
- 方法一:指针位移
int my_strlen(const char * str) { assert(str); int cnt = 0; while(*str) { cnt++; str++; } return cnt; }方法二:递归
int my_strlen(const char * str) { assert(str); if(*str == '\0') return 0; else { return 1 + my_strlen(str + 1); //可以写前置++str,但不可用后置str++ } }3.2 strcpy 和 strncpy
3.2.1 strcpy
使用前须添加头文件<string.h>
char * strcpy (char * destination, const char * source);
- 用法:可以对指定的字符串进行拷贝存放。
- 参数:destination为存放目标处的指针,source为拷贝源头的指针。
- 返回值:拷贝完成时返回目标空间的起始地址。
注意事项:
- 拷贝源头的字符串必须以 '\0' 作为结束标志。
- 拷贝源头字符串的 '\0' 也会一并拷贝。
- 目标空间必须足够大,可被修改。
使用示例:
#include<stdio.h> #include<string.h> int main() { char arr1[10] = {"hello"}; char arr2[20] = {0}; strcpy(arr2, arr1); printf("%s", arr2); return 0; }* 模拟实现strcpy:
char * my_strcpy(char * dst, const char * src) { assert(dst); assert(src); char * dst2 = dst; while(*src != '\0') { *dst = *src; dst++; src++; } //记得在dst末尾补充src所指向的'\0' *dst = *src; return dst2; }3.2.2 strncpy
使用前须添加头文件<string.h>
char * strncpy(char * destination, const char * source, size_t num);
- 用法:可以对指定长度的字符串进行拷贝存放。
- 参数:在strcpy的基础上增加了需要拷贝的字节数num。
其余部分与strcpy的用法大致相同,这里不作过多赘述。
与strcpy相比,strncpy指定了拷贝的长度,使得即使拷贝源头没有 '\0' 也可以正常使用,更加安全。
使用示例:
#include<stdio.h> #include<string.h> int main() { char arr1[15] = {"hello worldxx"}; char arr2[20] = {0}; strncpy(arr2, arr1, 11); printf("%s", arr2); return 0; }3.3 strcat 和 strncat
3.3.1 strcat
使用前须添加头文件<string.h>
char * strcat (char * destination, const char * source);
- 用法:将源字符串追加到指定的字符串后。
- 参数:destination为追加目标处的指针,source为源头数据的指针。
- 返回值:追加完成时返回目标空间的起始地址。
注意事项:
- 源字符串必须以 '\0' 结尾,否则会有越界风险。
- 目标空间必须也有 '\0' ,否则无法确定追加起点;追加时,源字符串的第一个元素会覆盖追加起点的 '\0' 。
- 目标空间必须足够大,可被修改。
使用示例:
#include<stdio.h> #include<string.h> int main() { char arr1[15] = {"hello world"}; char arr2[30] = {"haha "}; strcat(arr2, arr1); printf("%s", arr2); return 0; }3.3.2 strncat
使用前须添加头文件<string.h>
char * strncat (char * destination, const char * cource, size_t num);
- 用法:可以对指定长度的字符串进行追加。
- 参数:在strcpy的基础上增加了需要追加的字符数num。
- 返回值:追加完成时返回目标空间的起始地址。
注意事项:
- 追加结束会自动添加 '\0' ,即使未主动追加 '\0' 。
- 如果num超出了源字符串长度则会忽略掉。
使用示例:
#include<stdio.h> #include<string.h> int main() { char arr1[15] = {"hello world"}; char arr2[30] = {"haha "}; strncat(arr2, arr1, 5); printf("%s", arr2); return 0; }3.4 strcmp 和 strncmp
3.4.1 strcmp
使用前须添加头文件<string.h>
int strcmp (const char * str1, const char * str2);
用法:比较两个字符串,从两个字符串的第一个字符开始比较,如果两个字符的ASCII码值相等,就比较下一个字符,直至遇到不相等的字符或者字符串结束。
参数:str1,str2为两个字符串对应的指针。
返回值:若str1大于str2,返回大于0的整数;若str1等于str2,返回0;若str1小于str2,返回小于0的整数。
使用示例:
#include<stdio.h> #include<string.h> int main() { char str1[15] = {"hello C"}; char str2[15] = {"hello world"}; int ret = strcmp(str1, str2); printf("%d", ret); //由于C的ASCII码值小于w,所以打印结果为负整数 return 0; }3.4.2 strncmp
使用前须添加头文件<string.h>
int strncmp (char * str1, char * str2, size_t num);
- 用法:比较两个字符串中指定长度的大小。
- 参数:在strcmp的基础上增加了num,是比较的字符数。
- 返回值:与strcmp的规则相同。
与strcmp相比,strncmp指定了比较的长度,更加灵活、安全。
使用示例:
#include<stdio.h> #include<string.h> int main() { char str1[15] = {"hello C"}; char str2[15] = {"hello world"}; int ret = strncmp(str1, str2, 5); printf("%d", ret); //由于两个字符串的前5个字符相同,故返回0 return 0; }3.5 strstr
使用前须添加头文件<string.h>
char * strstr (const char * str1, const char * str2);
- 用法:在一个字符串中查找子字符串。
- 参数:str1为被查找的字符串指针,str2为需要查找的子字符串指针。
- 返回值:若查找成功,则返回第一次出现位置的指针,否则返回NULL。
若要以%s输出返回值的指针,则将会打印 '\0' 之前的所有字符。
使用示例:
#include<stdio.h> #include<string.h> int main() { char str2[15] = {"hello"}; char str1[15] = {"hello world"}; char * ret = strstr(str1, str2); if(ret != NULL) { printf("%s\n", ret); } else { printf("您所查找的字符串不存在。\n"); } //输出结果为hello world return 0; }3.6 strtok
使用前须添加头文件<string.h>
char * strtok (char * str, const char * delim);
- 用法:根据指定的分割字符delim,将str分割成一个个子字符串,并且原字符串中的分割符会用 '\0' 代替。
- 参数:str为需要分割的字符串,delim为指定的分割字符(可连续传入多个字符)。
- 返回值:成功时返回当前子字符串的指针,否则返回NULL。
- 使用步骤:首次调用需要传入待分割字符和分隔符,后续调用传入NULL和分隔符,当返回NULL时分割完成。
注意事项:
- 调用过程中会修改原字符串,可通过拷贝原字符串避免。
- 对于连续的分隔符,strtok会一并跳过。
- 不可传入空指针作为字符串。
使用示例:
#include<stdio.h> #include<string.h> int main() { char arr[] = "hello@.#world@@ni hao C yu.yan."; const char * str = NULL; //置空避免野指针 char cpy[30] = {0}; strcpy(cpy, arr); //将str中的字符串拷贝到cpy中,方便后续对cpy进行切割 const char * sep = "@#. "; //确定切割字符 for(str = strtok(arr, sep); str != NULL; str = strtok(NULL, sep)) { printf("%s\n",str); } return 0; }值得一提的是,如果子字符串较多,上述for循环的使用方式可以大大减少代码量:
- 初始化部分对arr进行首次传参;
- 判断条件部分设置为当str为NULL的时候停止循环;
- 增量部分将arr的部分用NULL取代,保证了后续调用时传参的准确性。
3.7 strerror
使用前须添加头文件<string.h>
char * strerror (int errnum);
- 用法:当库函数调用发生错误时,可以查看错误信息。
- 参数:errnum表示错误信息对应的整数,一般使用C语言程序中自带的全局变量errno。
- 返回值:错误信息字符串中首字符的地址。
注意事项:
- 关于errno:调用时须添加头文件<errno.h>,初始值为0,程序运行过程中若出错,则会更新为错误码。一般有以下几种错误码和对应的错误信息:
- 0:No error
- 1:Operation not permitted
- 2:No such file on dictionary
- 3:No such process
- 4:Interrupted function call
- 5:Input/output error
- 6:No such divice or address
- 7:Arg list too long
- 8:Exec format error
- 9:Bad file descriptor
- 10:No child processes
2. strerror仅支持在标准库中的函数出错后进行错误码转换。
3. 更加便捷的方式:perror函数。可以省去引用errno和strerror,直接输出对应的错误信息。返回值为参数部分的字符串,一个冒号加一个空格,错误信息。
使用示例:
#include<stdio.h> #include<string.h> #include<errno.h> int main() { FILE * pfile = NULL; pfile = fopen("unexist.ent","r") //以只读模式打开文件unexist.ent if(pfile == NULL) { printf("错误信息是:%s\n", strerror(errno)); //或者替换为: //perror("错误信息是"); return 1; } return 0; }