前言:在编程的过程中,我们经常要处理字符和字符串,为了⽅便操作字符和字符串,C语⾔标准库中提供了⼀系列库函数,接下来我们就学习⼀下这些函数。
———— 光景不待人,须臾发成丝
1. 字符分类函数
常见的字符串分类函数,包含在<ctype.h>这个头文件中
这些函数使用上都非常的类似,现在以islower函数为例子进行使用
int islower ( int c );
#include<stdio.h>
#include<ctype.h>
int main()
{
if (islower('a'))
{
printf("yes\n");
}
return 0;
}
2.字符转换函数
在c语言字符串库中提供了两个字符串转换函数:
1. int tolower ( int c ); //将参数传进去的⼤写字⺟转⼩写2. int toupper ( int c ); //将参数传进去的⼩写字⺟转⼤写
练习: 写⼀个代码,将字符串中的⼩写字⺟转⼤写,其他字符不变。
int main()
{
char arr[] = "I am a student ";
int i = 0;
while (arr[i] != '\0')
{
if (arr[i] >= 'a' && arr[i] < 'z')
arr[i] -= 32;
}
printf("%s", arr);
return 0;
}
通过使用库函数简化代码:
int main()
{
char arr[] = "I am a student ";
int i = 0;
while (arr[i] != '\0')
{
if (isupper(arr[i]))
{
arr[i] = tolower(arr[i]);
}
i++;
}
printf("%s", arr);
return 0;
}
3. strlen的使⽤和模拟实现
1. strlen的简介:
1.参数分析:
size_t strlen ( const char * str )参数一:待计算长度的字符串2.功能说明:
字符串以 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前⾯出现的字符个数(不包含'\0')。3.注意事项:strlen所求的字符必须是以\0结尾的字符串,strlen的返回值是无符号整形。
2.strlen的简单使用
#include<stdio.h>
int main()
{
char arr[]="hello world";
size_t num=strlen(arr);
printf("%zd",num);
return 0;
}
易错点一、忽视返回值为无符号整形
#include <stdio.h>
#include <string.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;
}
详解:
strlen(str2)
是 3,strlen(str1)
是 6,所以3 - 6 = -3
。但由于无符号整数的特性,这个 - 3 会被转换为一个很大的正数,导致条件strlen(str2) - strlen(str1) > 0
总是为真。
易错点二、忽视所求字符串无'\0'
char str[] = "Hello";
printf("%zd", strlen(str)); // 应输出5
char str[5] = {'H','e','l','l','o'}; // 无'\0'终止
printf("%zd", strlen(str)); // 未定义行为
3.strlen的三种模拟实现:
1.使用递归法:
//递归实现
size_t my_strlen1(const char* s)
{
assert(s != NULL);
//不创建临时变量,求字符串长度
if (*s == '\0')
return 0;
return 1 + my_strlen1(s + 1);
}
执行过程:
'h'→1 + my_strlen1("ello")
→1 + (1 + my_strlen1("llo"))
...
→1 + (1 + (1 + (1 + (1 + 0))))
= 5
2.使用计数器法:
size_t my_strlen2(const char* arr)
{
assert(arr != NULL);
size_t count = 0;
while (*arr != '\0')
{
arr++;
count++;
}
return count;
}
3.指针-指针
size_t my_strlen3(const char* arr)
{
assert(arr != NULL);
char* temp = arr;
while (*arr != '\0')
{
arr++;
}
return arr - temp;
}
4.strcpy的使用和模拟实现
1.strcpy的简介:
1.参数分析:
char* strcpy(char * destination, const char * source );
第一个参数为目标字符串,第二个参数为源字符串
2. 功能介绍:
将源 C 字符串指向的内容复制到目标数组指向的内容中,包括终止的空字符(并在该位置停止)。
3.注意事项:
1.源字符串必须以 '\0' 结束。
2.会将源字符串中的 '\0' 拷贝到⽬标空间。3.⽬标空间必须⾜够⼤,以确保能存放源字符串。4.⽬标空间必须可修改。
2.strcpy的简单使用:
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[] = "hello world";
char arr2[] = "xxxxxxxxxxxxx";
strcpy(arr2, arr1);
printf("%s\n", arr2);
return 0;
}
3.strcpy的模拟实现:
char* my_strcpy1(char* dest, const char* src)
{
assert(dest != NULL);
assert(src != NULL);
char* start = dest;
while (*src != '\0')
{
*dest = *src;
src++;
dest++;
}
*dest = *src;
return start;
}
char* my_strcpy2(char* dest, const char* src)
{
assert(dest != NULL);
assert(src != NULL);
char* strat = dest;
//dest++为后置自增,使用的值为自增前的值
//结束条件,当*src为\0时 赋值给*dest
//整个表达式就为\0 \0的ascii码值就是0,while循环终止
while (*dest++ = *src++)
{
;
}
return start;
}
5.strcat的使用和模拟实现
1.strcat的简介
1.参数分析
char * strcat ( char * destination, const char * source );
参数一:目标字符串 参数二:源字符串
返回类型为:char*
2 . 功能介绍:
将源字符串的副本追加到目标字符串,目标字符串中的终止空字符将被源字符串的第一
个字符覆盖,并且在目标字符串中由两个字符串连接形成的新字符串末尾包含一个空字符。
3.注意事项:
1.源字符串必须以 '\0' 结束。
2.⽬标字符串中也得有 \0 ,否则没办法知道追加从哪⾥开始。3.⽬标空间必须有⾜够的⼤,能容纳下源字符串的内容。4.⽬标空间必须可修改。5.字符串不能⾃⼰给⾃⼰追加
2.strcat的简单使用
#include<stdio.h>
int main()
{
char arr2[20] = "hello ";
char arr1[] = "world";
strcat(arr2, arr1);
printf("%s\n", arr2);
return 0;
}
源字符是从目标字符的\0位置开始追加得,通过下面一段代码即可进行验证:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
char arr2[20] = "he\0llo ";
char arr1[] = "world";
strcat(arr2, arr1);
printf("%s\n", arr2);
return 0;
}
字符串不能自己给自己追加的原因:
strcat
首先会定位到dest
的终止符'\0'
,然后将src
的内容复制到该位置。
若
dest
和src
指向同一内存区域,在复制过程中,src
的内容可能会被提前覆盖,导致不可预期的结果(如无限循环或内存损坏)。
例如:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
char * s = "abc";
strcat(s, s);
printf("%s\n", s);
return 0;
}
在追加的过程中将目标字符串的'\0'覆盖,从而导致源字符串无新的‘\0’,进行无限循环覆盖。
3.strcat的模拟实现
思路:
1.找到源字符串的‘\0’位置
2.将目标字符串从'\0'位置进行拷贝
//模拟实现strcat
char* my_strcat(char* dest, const char* src)
{
assert(dest != NULL);
assert(src != NULL);
char* start = dest;
// 1.找到目标空间的 '\0'
while (*dest != '\0')
{
dest++;
}
// 2. 进行数据的拷贝
while (*dest++ = *src++)
{
;
}
return start;
}
6.strcmp的使用和模拟实现
1.strcmp的简介
1.参数分析:
int strcmp ( const char * str1, const char * str2 );
返回值为:int
参数一:字符串1 参数二:字符串2
2.功能分析:
该函数从每个字符串的第一个字符开始比较。如果它们彼此相等,则继续比较后续的字
符对,直到字符不同或遇到终止空字符。
3.注意事项:
return value 返回值 indicates 指示 <0
the first character that does not match has a lower value in ptr1 than in ptr2
第一个不匹配的字符在 ptr1 中的值小于在 ptr2 中的值0
the contents of both strings are equal
两个字符串的内容相等>0
the first character that does not match has a greater value in ptr1 than in ptr2
第一个不匹配的字符在 ptr1 中的值大于在 ptr2 中的值
2.strcmp的简单使用
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[] = "abcd";
char arr2[] = "abcde";
printf("%d\n", strcmp(arr1, arr2));
return 0;
}
3.strcmp的模拟实现
//模拟实现strcmp
int my_strcmp(const char* str1, const char* str2)
{
//断言str1和str2都不为'\0'
assert(str1 && str2);
//用while循环,进行循环判断该组字符对是否相等,如果相等则继续比对下一组字符对
while (*str1 == *str2)
{
if (*str1 == '\0') return 0; //两者相等且都为'\0'时返回0
str1++;
str2++;
}
//当两者第一次不相等时,比较不相等的字符的ASCII码值
//1.如果*str1>*str2则返回1
//2.反之*str1<*str2则返回-1
if (*str1 > *str2) return 1;
else return -1;
}
7.strncpy
1.strncpy的简介
1.参数分析:
char * strncpy ( char * destination, const char * source, size_t num );
参数一:destination 指向目标数组的指针,其中内容要复制到该数组中,该数组的空间应该满足足够大。
参数二:source 源 C 字符串。
参数三:num 从源字符串中复制的最大字符数,其中size_t 是一个无符号整数类型。
返回类型:char*
2.函数功能:
拷⻉num个字符从源字符串到⽬标空间。
3.注意事项:
1.如果源字符串的⻓度⼩于num,则拷⻉完源字符串之后,在⽬标的后边追加0,直到
num个。
2.如果源字符串的⻓度大于num,则拷贝源字符串的前num个到目标字符串中。
2.strncpy的简单使用
情况一:num小于源字符串
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[] = "abcdef";
char arr2[] = "xxxxxxxxx";
strncpy(arr2, arr1, 3);
printf("%s", arr2);
return 0;
}
情况二:num大于源字符串
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[] = "abcdef";
char arr2[] = "xxxxxxxxx";
strncpy(arr2, arr1, 10);
printf("%s", arr2);
return 0;
}
8.strncat
1.strncat的简介
1.参数分析:
char * strncat ( char * destination, const char * source, size_t num );
参数一:指向目标数组的指针,该数组应包含一个 C 字符串,并且足够大以包含连接后的结果字符串,包括额外的空字符。
参数二:要追加的 C 字符串。
参数三: 要追加的最大字符数,size_t 是一个无符号整数类型。
返回类型:char* 返回追加后的字符串
2.功能介绍:
将源字符串的前 num 个字符追加到目标字符串,并加上一个终止空字符,如果源 C 字符串的长度小于 num,则仅复制到终止空字符的内容。
3.注意事项:
目标字符串的空间应该足够大。
2.strncat的简单使用
情况一:num小于源字符串
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[20] = "hello ";
char arr2[] = "world";
strncat(arr1, arr2, 3);
printf("%s", arr1);
return 0;
}
情况二:num大于源字符串
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[20] = "hello ";
char arr2[] = "world";
strncat(arr1, arr2, 10);
printf("%s", arr1);
return 0;
}
9.strncmp
1.strncmp的简介
1.参数分析:
int strncmp ( const char * str1, const char * str2, size_t num );
参数一:str1要比较的 C 字符串
参数二:str2要比较的 C 字符串。
参数三:num要比较的最大字符数,size_t 是一个无符号整数类型。
返回类型:int
2.功能介绍:
该函数从每个字符串的第一个字符开始比较。如果它们相等,则继续比较后续的字符对,直到字符不同、遇到终止空字符或两个字符串中有 num 个字符匹配(以先发生者为准)。
3.注意事项:
return value 返回值 indicates 指示 <0
the first character that does not match has a lower value in str1 than in str2
第一个不匹配的字符在 str1 中的值小于在 str2 中的值0
the contents of both strings are equal
两个字符串的内容相等>0
the first character that does not match has a greater value in str1 than in str2
第一个不匹配的字符在 str1 中的值大于在 str2 中的值
2.strncmp的简单使用
情况一:num为0,默认两个字符串相同
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abcdefghi";
int ret = strncmp(arr1, arr2, 0);
printf("%d\n", ret);
return 0;
}
情况二:比较字符串的前num个
define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abcdefghi";
int ret = strncmp(arr1, arr2, 15);
printf("%d\n", ret);
return 0;
}
10.strstr的使用和模拟实现
1.strstr的简介
1.参数分析:
char * strstr ( const char * str1, const char * str2 );
参数一:待扫描的 C 字符串。
参数二:包含要匹配的字符序列的 C 字符串。
返回类型:char* :指向 str1 中 str2 指定字符序列第一次出现的位置的指针,如果序列在 str1 中不存在,则返回空指针。
2.功能介绍:
返回 str1 中 str2 第一次出现的位置的指针,如果 str2 不是 str1 的一部分,则返回空指针。
2.strstr的简单使用
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
char arr1[] = "This is an apple\n";
const char* p = "is";
char * ret = strstr(arr1, p);
printf("%s", ret);
return 0;
}
3.strstr的模拟实现
函数功能:
基于函数功能在str1中查找str2的第一次出现的位置,并返回该位置的指针,如果序列中不存在该字符串str2则返回空指针。
一般分为以下几种情况:
1.简单匹配:abcde abc2.复杂匹配:abbbcdef bbc3.找不到:abce bbc4.str2为空字符: abce "" 空字符串直接返回str1的当前位置基于这第二种情况,我们可以规定查找的变量:1.cur=‘a’ 时 *s1!=*s2 不进行往后继续查找 cur++2.cur='b' 时 *s1==*s2 进行循环往后查找 s1++,s2++直到出现s1='b' s2='c'时停止继续查找 cur++,s2重新归为str2的位置....当cur查找到‘\0’的时候,或则s2查找到‘\0’的时候,说明所有查找结束。
char* my_strstr2(const char* str1, const char* str2)
{
const char* s1 = NULL;
const char* s2 = NULL;
const char* cur = str1;
//字串为空返回原字符串
if (*str2 == '\0') return (char*)cur;
while (cur != '\0')
{
s1 = cur;//更新查找的位置
s2 = str2; //从str2开始
while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)
{
s1++;
s2++;
}
//1.字串遇到'\0' ---刚好找到
//2.原字符串遇到'\0' ---刚好找到或找不到
//3.*s1与*s2的字符不相等 ---cur++
if (*s2 == '\0')
return (char*)cur;
cur++;
}
return NULL;
}
注意事项:
*s1 != '\0'
:确保主字符串指针s1
没有到达字符串末尾。如果s1
已经指向字符串结束符,说明主字符串剩余长度不足,无需继续比较。*s2 != '\0'
:确保子字符串指针s2
没有到达子字符串末尾。如果s2
先到达结束符,说明子字符串已全部匹配成功。
11.strtok的使用
1.strtok的简介
1.参数分析:
char * strtok ( char * str, const char * sep );
参数一:str是待切割的字符串(包含了多个sep存储的分隔符)
参数二:sep存储分隔符的字符串
返回类型:char*
例如:zpengwei@yeah.net
char arr[ ] = "zpengwei@yeah.net";
char sep[ ] = "@.";
2.功能分析:
将字符串根据标记的位置进行分割
2.strtok的简单使用
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
//例如:zpengwei@yeah.net
char arr[] = "zpengwei@yeah.net";
char sep[] = "@.";
//strtok函数的第一个参数不为空指针,将找到str的第一个标记'@'
//"zpengwei \0 yeah . net"
//将分割字符改为'\0',并返回切割后字符串"zpengwei"的地址
char* ret1 = strtok(arr, sep);
printf("%s\n", ret1);
//strtok函数的第一个参数为空指针时,将在同一个字符串中被保存的位置开始,查找下一个标记
//"zpengwei \0 yeah \0 net"
//继续将分割字符改为'\0',并返回切割后字符串"yeah"的地址
char* ret2 = strtok(NULL, sep);
printf("%s\n", ret2);
char* ret3 = strtok(NULL, sep);
printf("%s\n", ret3);
//如果字符串不存在更多的分隔符,则返回NULL指针
return 0;
}
通过循环进行简化代码的实现
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
char arr[] = "zpengwei@yeah.net";
char sep[] = "@.";
for (char* ret = strtok(arr, sep); ret != NULL; ret = strtok(NULL, sep) )
{
printf("%s\n", ret);
}
return 0;
}
1.循环起始条件为:char* ret = strtok(arr, sep)
首次调用:传入原始字符串
arr
和分隔符字符串sep
,函数会找到第一个分隔符,并将其替换为\0
,同时返回指向分割出的第一个子字符串的指针。
2.循环调整部分为:ret = strtok(NULL, sep)
后续调用:传入
NULL
和相同的分隔符sep
,函数会继续从上次中断的位置开始寻找下一个分隔符,直到字符串结束。
3.循环终止条件为:ret!=NULL
当
strtok
找不到更多分隔符时(即到达字符串末尾),它会返回NULL
。
12.strerror
1.strerror的简介
1.参数分析:
char * strerror ( int errnum );
参数一: Errno number 错误编号。
返回类型:char*
2.函数功能:
解释 errnum 的值,生成一个包含描述错误条件的消息的字符串,该错误条是由
中 errno所控制的。
3.注意事项:
对于errno这是在库中的一个全局变量,errno的每个值都会对应一个相应的字符串信息,
通过strerror这个函数就可以获取得到这个消息。
2.strerror的简单使用
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
//errno.h中定义了一个全局变量errno 默认值为0
//在使用函数发生某种错误时,就会将对应的错误码放在errno中,需要通过strerror进行获取这个错误码
//把错误码对应的错误信息的字符串地址返回
//可以通过printf( " %s ", strerror(i) )进行打印
for (int i = 0; i < 10; i++)
{
printf("%s\n", strerror(i));
}
return 0;
}
简单的案例:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
FILE* pf=fopen("test.txt","r");
if (pf == NULL)
{
printf("%s\n", strerror(errno));
//perror("zhangshan");
return 1;
}
fclose(pf);
return 0;
}