⽬录:
(1)字符分类函数
(2)字符转换函数
(3)strlen的使用和模拟实现
(4)strcpy的使用和模拟实现
(5)strcat的使用和模拟实现
(6)strcmp的使用和模拟实现
(7)strncpy函数的使用
(8)strncat函数的使用
(9)strncmp函数的使用
(10)strstr的使用和模拟实现
(11)strtok函数的使用
(12)strerror函数的使用
(13)perror函数的使用
在编程的过程中,我们经常要处理字符和字符串,为了⽅便操作字符和字符串,C语⾔标准库中提供了 ⼀系列库函数,接下来我们就学习⼀下这些函数。
先区别一下各种0:
0 --- 数字0;
'0' --- 数字字符0 --- ASCII码值是48
\0 --- \ddd形式 --- 特殊转义字符 --- ddd表示1~3个8进制数字 --- \0 --- 就是一个八进制0 --- 就是0 --- ASCII码值为0
NULL --- 指针专属的0
1. 字符分类函数
C语言中有⼀系列的函数是专门做字符分类的,也就是⼀个字符是属于什么类型的字符的。 这些函数的使⽤都需要包含⼀个头文件是 ctype.h
函数 | 如果它的参数符合下列条件就返回真 |
---|---|
iscntrl | 任何控制字符 |
isspace | 空白字符:空格' ',换页'\t', 换行'\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 | 任何可打印字符,包括图形字符和空白字符 |
这些函数的使用方法非常类似,我们就讲解⼀个函数的事情,其他的非常类似:
int islower ( int c );//传递的是字母或者字母的ASCII码值
islower 是能够判断参数部分的 c 是否是小写字母的。 通过返回值来说明是否是小写字母,如果是小写字母就返回非0的整数,如果不是小写字母,则返回0;
//写一个代码,将字符串中的小写字母转大写,其他字符不变 #include <stdio.h> #include <ctype.h> int main () { int i = 0; char str[] = "Test String.\n"; char c; while (str[i]) { c = str[i]; if (islower(c)) c -= 32; putchar(c); i++; } return 0 }
2. 字符转换函数
C语言提供了两个字符转换函数
int tolower(int c);//将参数传进去的大写字母转小写字母 int toupper(int c);//将参数传进去的小写字母转大写字母
用例:
#include <stdio.h> #include <ctype.h> 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; }
接下来的都是字符串函数
3. strlen的使用和模拟实现
3.1 strlen的使用
size_t strlen ( const char * str );
1.字符串以'\0'作为结束标志,strlen函数返回的实在字符串中'\0'前面出现的字符个数(不包含'\0')
2.参数指向的字符串必须要以'\0'结束
3.注意函数的返回值为size_t,是无符号的(易错)
4.strlen的使用必须包含头文件string.h
3.2 strlen的模拟实现
方式1:
//计数器⽅式 int my_strlen(const char * str) { int count = 0; assert(str != NULL); //包含assert.h头文件 while(*str) { count++; str++; } return count; }
方式2:递归
//不能创建临时变量计数器 int my_strlen(const char * str) { assert(str != NULL); if(*str == '\0') return 0; else return 1+my_strlen(str+1); }
方式3:
//指针-指针的⽅式 int my_strlen(char *s) { assert(str != NULL); char *p = s; while(*p != '\0' ) p++; return p-s; }
4. strcpy的使用和模拟实现
4.1 strcpy的使用
char * strcpy ( char * destination, const char * source );
复制字符串
将 source 指向的 C 字符串复制到 destination 指向的数组中,包括终止 null 字符(并在该点停止)。
为避免溢出,destination 指向的数组的大小应足够长,以包含与 source 相同的 C 字符串(包括终止 null 字符),并且不应在内存中与 source 重叠。
strcpy函数返回的是目标空间的起始地址。
注意:
(1)源头的字符串中必须包含\0,没有\0的话,strcpy不能结束!
(2)目标空间必须足够大,避免溢出
例:
#include <stdio.h> #include <string.h> int main () { char str1[]="Sample string"; char str2[40]; char str3[40]; strcpy (str2,str1); strcpy (str3,"copy successful"); printf ("str1: %s\nstr2: %s\nstr3: %s\n",str1,str2,str3); return 0; }
易错类型:
int main() { char arr1 = "hello world"; char* p = "xxxxxxxxxxxxxxxxxxxxxx";//常量字符串不能被修改,否则程序会崩 strcpy(p,arr1);//err printf("%s\n",p); return 0; }
4.2 strcpy的模拟实现
char* my_strcpy(char *dest, const char*src) { char *ret = dest; assert(dest != NULL); assert(src != NULL); while((*dest++ = *src++))// '\0'的ASCII值是0 { ; } return ret;//strcpy函数返回的是目标空间的起始地址 } int main() { char arr1[] = "abcdef"; char arr2[20] = {0}; my_strcpy(arr2,arr1); printf("%s\n",arr2);//打印abcdef printf("%s\n",my_strcpy(arr2,arr1));//打印abcdef return 0; }
5. strcat的使用和模拟实现
5.1 strcat的使用
char * strcat ( char * destination, const char * source );
连接字符串
将源字符串的副本附加到目标字符串。destination 中终止的 null 字符将被 source 的第一个字符覆盖,并且 null 字符包含在 destination 中由两者串联形成的新字符串的末尾。
destination 和 source 不得重叠。
-
destination
Pointer to the destination array, which should contain a C string, and be large enough to contain the concatenated resulting string.
指向目标数组的指针,该数组应包含 C 字符串,并且足够大以包含串联的结果字符串。
-
source
C string to be appended. This should not overlap destination.
C 字符串。这不应与 destination 重叠。
注意:
(1)返回的也是目标空间的起始地址
(2)目标空间必须足够大,要能再容纳下源字符串的内容
(3)源字符串必须以\0结束
(4)目标字符串也得有\0,否则没办法知道追加从哪里开始
(5)目标空间必须可以修改(不能是常量字符串)
例:
#include <stdio.h> #include <string.h> int main () { char str[80]; strcpy (str,"these "); strcat (str,"strings "); strcat (str,"are "); strcat (str,"concatenated."); puts (str);//打印these strings are concatenated. return 0; }
3.2 strcat的模拟实现
char *my_strcat(char *dest, const char*src) { char *ret = dest; assert(dest != NULL); assert(src != NULL); //1.找到目标空间的\0 while(*dest) { dest++; } //2.拷贝 while((*dest++ = *src++)) { ; } return ret;//返回目标空间的起始地址 } int main() { char arr1[20] = "hello "; char arr2[] = "world"; my_strcat(arr1,arr2);//字符串追加 printf("%s\n",arr1); return 0; }
易错点:不能strcat(arr,arr); \0被覆盖了,某些编译器会死循环,strcat不保证自己给自己追加能顺利完成
6. strcmp的使用和模拟实现
6.1 strcmp的使用
int strcmp ( const char * str1, const char * str2 );
比较两个字符串
将 C 字符串 str1 与 C 字符串 str2 进行比较。
此函数开始比较每个字符串的第一个字符。如果它们彼此相等,则继续处理以下对,直到字符不同或到达终止 null 字符。注意:比较的不是字符串的长度,而是字符串对应位置上的字符(ASCII码值)的大小
此函数执行字符的二进制比较。有关考虑特定于区域设置的规则的函数,请参阅 strcoll。
-
str1
要比较的 C 字符串。
-
str2 的
要比较的 C 字符串。
返回一个整数值,该值指示字符串之间的关系:
返回值 | 表明 |
---|---|
<0 | 第一个不匹配的字符在 PTR1 中的值低于 PTR2 中的值 |
0 | 两个字符串的内容相等 |
>0 | 第一个不匹配的字符在 PTR1 中的值大于 PTR2 中的值 |
用例:
#include <stdio.h> #include <string.h> int main () { char key[] = "apple"; char buffer[80]; do { printf ("Guess my favorite fruit? "); fflush (stdout); scanf ("%79s",buffer); } while (strcmp (key,buffer) != 0); puts ("Correct answer!"); return 0; }
• 标准规定:
(1)第⼀个字符串大于第⼆个字符串,则返回大于0的数字
(2)第⼀个字符串等于第⼆个字符串,则返回0
(3)第⼀个字符串小于第⼆个字符串,则返回小于0的数字
(4)那么如何判断两个字符串?比较两个字符串中对应位置上字符ASCII码值的大小
6.2 strcmp的模拟实现
int my_strcmp (const char * str1, const char * str2) { int ret = 0 ; assert(str1 != NULL); assert(str2 != NULL); while(*str1 == *str2) { if(*str1 == '\0') return 0; str1++; str2++; } return *str1-*str2; } int main() { char arr1[] = "abcdef"; char arr2[] = "abcde"; int ret = my_strcmp(arr1,arr2); printf("%d\n",ret); return 0; }
对于7 8 9中
strncpy strncat strncmp是长度受限制的字符串函数;
strcpy strcat strcmp是长度不受限制的字符串函数
7. strncpy函数的使用
char * strncpy ( char * destination, const char * source, size_t num );
从字符串中复制字符
将源的前 number 个字符复制到目标。如果在复制 num 个字符之前找到源 C 字符串的末尾(由 null 字符表示),则目标将填充零,直到写入总数 num 个字符为止。
如果 source 长于 num,则不会在 destination 的末尾隐式附加 null 字符。因此,在这种情况下, destination 不应被视为以 null 结尾的 C 字符串(因此读取它会溢出)。
destination 和 source 不应重叠(参见 memmove 了解重叠时更安全的替代方案)。
destination:指向要复制内容的目标数组的指针。
source:要复制的 C 字符串。
num:要从source复制的最大字符数。size_t 是无符号整型。
用例:
#include <stdio.h> #include <string.h> int main () { char str1[]= "To be or not to be"; char str2[40]; char str3[40]; /* copy to sized buffer (overflow safe): */ strncpy ( str2, str1, sizeof(str2) ); /* partial copy (only 5 chars): */ strncpy ( str3, str2, 5 ); str3[5] = '\0'; /* null character manually added */ puts (str1); puts (str2); puts (str3); return 0; }
如果source里面的内容不够num,拷贝完源字符串之后,在目标的后边追加0,直到num个
8. strncat函数的使用
char * strncat ( char * destination, const char * source, size_t num );
从字符串附加字符
将 source 的前 num 个字符附加到 destination,外加一个终止 null 字符。
char arr[20] = "abcdef\0yyyyy"; char arr1[20] = "xxxxxxxx"; strncat(arr,arr1,3);//arr1 变成"abcdefxxx\0yy"(xxx后面带了\0,yy最后也有\0)
如果 source 中 C 字符串的长度小于 num,则仅复制终止 null 字符之前的内容。
destination:指向目标数组的指针,该数组应包含 C 字符串,并且足够大以包含串联的结果字符串,包括其他 null 字符。
source:C 字符串。
num:要附加的最大字符数。size_t 是无符号整型。
用例:
#include <stdio.h> #include <string.h> int main () { char str1[20]; char str2[20]; strcpy (str1,"To be "); strcpy (str2,"or not to be"); strncat (str1, str2, 6); puts (str1); return 0; }
9. strncmp函数的使用
int strncmp ( const char * str1, const char * str2, size_t num );
比较两个字符串的字符
比较 C 字符串 str1 的字符数与 C 字符串 str2 的字符数。 此函数开始比较每个字符串的第一个字符。如果它们彼此相等,则继续处理以下对,直到字符不同;到到达终止 null 字符;或者直到两个字符串中的 num 个字符匹配,以先发生者为准。
str1:要比较的 C 字符串。
str2:要比较的 C 字符串。
num:要比较的最大字符数。size_t 是无符号整型。
返回一个整数值,该值指示字符串之间的关系:
返回值 | 表明 |
---|---|
<0 | 第一个不匹配的字符在 str1 中的值低于 str2 中的值 |
0 | 两个字符串的内容相等 |
>0 | 第一个不匹配的字符在 str1 中的值大于在 str2 中的值 |
用例:
#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; }
上述函数会有若干函数被vs2022认为不安全,指定了num,但目标空间可能不足,存在危险
10. strstr的使用和模拟实现
10.1 strstr的使用
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 作为结束标志)。
功能:在str1中找str2这个字符串,第一次出现的位置;
如果找到了,就返回第一次出现的起始地址;
如果找不到,就返回NULL;
#include<stdio.h> #include<string.h> int main() { char arr[] = "abcdefabcdef"; char* p = "efab"; char* p1 = "cdef"; char* ret = strstr(arr,p); char* ret1 = strstr(arr,p1); printf("%s\n",ret);//打印efabcdef printf("%s\n",ret);//打印cdefabcdef return 0; }
10.2 strstr的模拟实现
const 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); }//当前写的模拟实现不是最优算法 //KMP算法 —— 字符串中找字符串
11. strtok函数的使用(用起来很奇怪很抽象的一个函数)
char * strtok ( char * str, const char * sep );
-
sep参数指向⼀个字符串,定义了用作分隔符的字符集合
-
第⼀个参数指定⼀个字符串,它包含了0个或者多个由sep字符串中⼀个或者多个分隔符分割的标 记。
-
strtok函数找到str中的下⼀个标记,并将其用 \0 结尾,返回⼀个指向这个标记的指针。(注: strtok函数会改变被操作的字符串,所以被strtok函数切分的字符串⼀般都是临时拷贝的内容并且 可修改。)
-
strtok函数的第⼀个参数不为NULL,函数将找到str中第⼀个标记,strtok函数将保存它在字符串
-
strtok函数的第⼀个参数为 NULL ,函数将在同⼀个字符串中被保存的位置开始,查找下⼀个标记。
-
如果字符串中不存在更多的标记,则返回 NULL 指针
返回值:
如果找到令牌,则为指向令牌开头的指针。 否则,为 null 指针。 当在正在扫描的字符串中到达字符串的末尾(即 null 字符)时,始终返回 null 指针。
用例:
#include <stdio.h> #include <string.h> int main () { char arr[] = "sadlkfjlas@sadf.net"; char buf[256] = {0}; srecpy(buf,arr); char* sep = "@."; char* ret = strtok(buf,sep);//sadlkfjlas\0sadf\0net printf("%s\n",ret);//打印sadlkfjlas ret = strtok(NULL,sep); printf("%s\n",ret);//打印sadf ret = strtok(NULL,sep); printf("%s\n",ret);//打印net return 0; }
一个巧妙的写法:
#include <stdio.h> #include <string.h> int main () { char arr[] = "sadlkfjlas@sadf.net"; char buf[256] = {0}; srecpy(buf,arr); char* sep = "@."; char* ret = NULL; for(ret=strtok(buf,sep);ret != NULL;ret=strtok(NULL,sep)) { printf("%s\n",ret); } return 0; }
这个函数有记忆功能,每次调用完之后会保存一个地址(有static修饰的变量)
12. strerror函数的使用
char * strerror ( int errnum );
strerror 函数可以把参数部分错误码对应的错误信息的字符串地址返回来。 在不同的系统和C语言标准库的实现中都规定了⼀些错误码,⼀般是放在 errno.h 这个头文件中说明 的,C语言程序启动的时候就会使用⼀个全局的变量errno来记录程序的当前错误码,只不过程序启动 的时候errno是0,表⽰没有错误,当我们在使用标准库中的函数的时候发生了某种错误,就会将对应 的错误码,存放在errno中,而⼀个错误码的数字是整数很难理解是什么意思,所以每⼀个错误码都是 有对应的错误信息的。strerror函数就可以将错误对应的错误信息字符串的地址返回。
errno —— c语言的全局变量
调用c语言的库函数
用例1:
#include <stdio.h> #include <string.h> #include <errno.h> int main () { FILE * pFile; pFile = fopen ("unexist.ent","r"); if (pFile == NULL) printf ("Error opening file unexist.ent: %s\n",strerror(errno)); return 0; }
用例2:
int main() { int i = 0; for(i = 0;i <= 10;i++) { printf("%d:%s\n",i,strerror(i)); } return 0; }
用例3:
#include<stdio.h> #include<string.h> #include<errno.h> int main() { //打开文件 FILE* pf = fopen("data.txt","r");//"r" - 读,以文件的形式,打开文件,如果文件不存在就打开失败 if(pf == NULL) { printf("打开文件失败,原因是:%s",strerror(errno)); return 1; } else { printf("打开文件成功\n"); //... fclose(pf); pf = NULL; } return 0; }
strerror —— 将错误码对应的错误信息的字符串的起始地址返回
perror —— 将errno中错误对应的错误信息直接打印出来
13. perror函数的使用
void perror ( const char * str );
打印错误消息
perror 函数先打印str函数指向的字符串,再打印冒号,再打印一个空格,再打印错误对应的错误信息
将 errno 的值解释为错误消息,并将其打印到 stderr(标准错误输出流,通常是控制台),可选择在其前面加上 str 中指定的自定义消息。
errno 是一个整型变量,其值描述调用库函数产生的错误条件或诊断信息(C 标准库的任何函数都可以为 errno 设置值,即使此引用中未明确指定,即使没有发生错误),请参阅 errno 了解更多信息。
perror
生成的错误消息取决于平台。
如果参数 str 不是 null 指针,则打印 str 后跟冒号 (:) 和空格。然后,无论 str 是否为空指针,都会打印生成的错误描述,后跟换行符 ('\n'
)。
perror
应在产生错误后立即调用,否则它可能会被对其他函数的调用覆盖。
用例:
#include <stdio.h> int main () { FILE * pFile; pFile=fopen ("unexist.ent","rb"); if (pFile==NULL) perror ("The following error occurred"); else fclose (pFile); return 0; }