【C语言】字符函数与字符串函数实战:用法原理 + 模拟实现

在编程的过程中,我们经常要处理字符和字符串,为了方便操作字符和字符串,C语言标准库中提供了一系列库函数,接下来我们就学习一下这些函数

一、字符分类函数

C语言中有一系列的函数是专门做字符分类的,也就是一个字符是属于什么类型的字符的。这些函数的使用都需要包含一个头文件是ctype.h

这些函数的使用方法非常类似,我们就讲解一个函数的事情,其他的非常类似:

int islower ( int c );

islower 是能够判断参数部分的 c 是否是小写字母的。通过返回值来说明是否是小写字母,如果是小写字母就返回非0的整数,如果不是小写字母,则返回0。

练习:写⼀个代码,将字符串中的小写字母转大写,其他字符不变。

#include<stdio.h>
#include<ctype.h>
int main()
{
	char arr[] = "Hello World";

	//因为字符串放在数组中他是有对应下标的,0也算一个
	//将字符串中的字符逐个进行处理
	//遇到小写,转换成大写,再输出
	//如果不是小写,正常输出
	int i = 0;
	while (arr[i] != '\0')
	{
		if (islower(arr[i]))
		{
			arr[i] = arr[i] - 32;
		}
		printf("%c", arr[i]);
		i++;
	}
	return 0;
}

二、字符转换函数

C语言提供了俩个字符转换函数

int tolower ( int c ); //将参数传进去的⼤写字⺟转⼩写  
int toupper ( int c ); //将参数传进去的⼩写字⺟转⼤写 

上面的代码,我们将小写转大写,是-32完成的效果,有了转换函数,就可以直接使用toupper函数。

#include<stdio.h>
#include<ctype.h>
int main()
{
	char arr[] = "Hello World";
	int i = 0;
	while (arr[i] != '\0')
	{
		if (islower(arr[i]))
		{
			arr[i] = toupper(arr[i]);
		}
		printf("%c", arr[i]);
		i++;
	}
	return 0;
}

三、strlen函数的使用和模拟实现

函数声明

  • 声明:size_t strlen(const char *str);

功能

  • 统计参数 str 指向的字符串的长度。统计的是字符串中 '\0' 之前的字符的个数。

参数

  • str:指针,指向了要统计长度的字符串。

返回值

  • 返回 str 指向的字符串的长度,返回的长度不会是负数,所以返回类型是 size_t
1、代码演示
#include<stdio.h>
#include<string.h>
int main()
{
	const char* str = "abcdef";
	printf("%zu\n", strlen(str));
	return 0;
}

使用注意事项

  • 字符串以 '\0' 作为结束标志,strlen 函数返回的是在字符串中 '\0' 前面出现的字符个数(不包含 '\0')。
  • 参数指向的字符串必须要以 '\0' 结束。
  • 注意函数的返回值为 size_t,是无符号的(易错)。
  • strlen 的使用需要包含头文件 <string.h>
2、strlen返回值
#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("str1 > str2\n");
	}
	return 0;
}

大家乍一看肯定都会觉得打印的肯定是 str1 > str2 因为这是我们学习前面的知识之后所就会默认觉得的内容,实则打印的结果是:str2 > str1这是因为strlen函数的返回值是size_t类型的(无符号整型),我们用俩个size_t类型的数据相减,其结果也会返回一个size_t类型的数据。为了避免这种问题,应该直接比较两个长度,而不是进行减法运算。

如果我们就是想要返回我们认为的 str1 > str2,我们可以采取强制类型转换,将他转换为int类型。

​
#include<stdio.h>
#include<string.h>
int main()
{
	const char* str1 = "abcdef";
	const char* str2 = "bbb";
	if ((int)strlen(str2) - (int)strlen(str1) > 0)
	{
		printf("str2 > str1\n");
	}
	else
	{
		printf("str1 > str2\n");
	}
	return 0;
}
3、模拟实现
方法一:计数器方式
#include<stdio.h>
#include<assert.h>
size_t my_strlen(const char* str)
{
	int count = 0;
	assert(str);
	while (*str)
	{
		count++;
		str++;
	}
	return count;
}
int main()
{
	char arr[] = "Hello World";
	size_t len = my_strlen(arr);
	printf("%zu\n", ret);
	return 0;
}
方法二:递归
#include<stdio.h>
size_t my_strlen(const char* str)
{
	if (*str != '\0')
		return 1 + my_strlen(str + 1);
	else
		return 0;
}
int main()
{
	char arr[] = "Hello World";
	size_t len = my_strlen(arr);
	printf("%zu\n", len);
	return 0;
}
方法三:指针 - 指针
#include<stdio.h>
#include<assert.h>
size_t my_strlen(const char* str)
{
	assert(str);
	char* p = str;
	while (*p != '\0')
		p++;
	return p - str;
}
int main()
{
	char arr[] = "Hello World";
	size_t len = my_strlen(arr);
	printf("%zu\n", len);
	return 0;
}

四、strcpy函数的使用和模拟实现

函数声明

  • 声明: char* strcpy(char * destination, const char * source );

功能

  • 字符串拷贝,拷贝到源头字符串中的 \0 为止

参数

  • destination:指针,指向目的地空间
  • source:指针,指向源头数据

返回值

  • 返回目标空间的起始地址,返回类型是 char*
1、代码演示
#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[20] = { 0 };
	char arr2[] = "hello world";
	//我们可能会想到不用这个函数直接使用循环的方式
	//arr1 = arr2 (这是不能使用的)
	//因为数组名是地址,地址是一个编号
	//地址是一个常量值,不能被修改
	//地址是指向空间的,但是地址不是空间
	char* ret = strcpy(arr1, arr2);
	printf("%s\n", ret);
	return 0;
}

使用注意事项

  • 源字符串必须以 '\0' 结束。
  • 会将源字符串中的 '\0' 拷贝到目标空间。
  • 目标空间必须足够大,以确保能存放源字符串。
  • 目标空间必须可修改。
2、模拟实现
test1(基础版)
#include<stdio.h>
char* my_strcpy(char* dest, const char* src)
//dest不能加const,因为他要去被修改
{
	char* ret = dest;
	//拷贝\0前面的字符
	while (*src != '\0')
	{
		*dest = *src;
		dest++;
		src++;
	}
	*dest = *src;//拷贝\0
	return ret;
}
int main()
{
	char arr1[] = "hello world";
	char arr2[20] = "******************";
	my_strcpy(arr2, arr1);
	printf("%s\n", arr2);
}
test2(升级版)
#include <stdio.h>
#include <assert.h>
//1.参数顺序 
//2.函数的功能,停⽌条件 
//3.assert
//4.const修饰指针 
//5.函数返回值 
char* my_strcpy(char* dest, const char* src)
{
	char* ret = dest;
	assert(dest != NULL);
	assert(src != NULL);

	while ((*dest++ = *src++))
	{
		;//先使用再++
	}
	return ret;//返回目标空间的起始地址
}
int main()
{
	char arr1[10] = { 0 };
	char arr2[] = "hello";
	my_strcpy(arr1, arr2);
	printf("%s\n", arr1);

	return 0;
}

五、strcat函数的使用和模拟实现

函数声明

  • 声明: char * strcat ( char * destination, const char * source );

功能

  • 字符串追加,把 source 指向的源字符串中的所有字符都追加到 destination 指向的空间中。

参数

  • destination:指针,指向目的地空间
  • source:指针,指向源头数据

返回值

  • 返回目标空间的起始地址,返回类型是 char*
1、代码演示
#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[20] = "hello";
	char arr2[] = " world";
	strcat(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

使用注意事项

  • 源字符串必须以 '\0' 结束。
  • 目标字符串中也得有 '\0',否则没办法知道追加从哪里开始。
  • 目标空间必须足够大,能容纳下源字符串的内容。
  • 目标空间必须可修改。
2、模拟实现
#include<stdio.h>
#include<assert.h>
char* my_strcat(char* dest, const char* src)
{
	char* ret = dest;
	assert(dest != NULL);
	assert(src != NULL);
	//找到目标空间中的第一个\0
	while (*dest)
	{
		dest++;
	}
	//从\0后面的位置开始追加
	while ((*dest++ = *src++))
	{
		;
	}
	return ret;
}
int main()
{
	char arr1[20] = "hello";
	char arr2[] = " world";
	my_strcat(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

六、strcmp函数的使用和模拟实现

函数声明

  • 声明: int strcmp ( const char * str1, const char * str2 );

功能

  • 用来比较 str1 和 str2 指向的字符串,从两个字符串的第一个字符开始比较,如果两个字符的 ASCII 码值相等,就比较下一个字符。直到遇到不相等的两个字符,或者字符串结束。

参数

  • str1:指针,指向要比较的第一个字符串
  • str2:指针,指向要比较的第二个字符串

返回值

  • 标准规定:
    • 第一个字符串大于第二个字符串,则返回大于 0 的数字
    • 第一个字符串等于第二个字符串,则返回 0
    • 第一个字符串小于第二个字符串,则返回小于 0 的数字
1、代码演示
#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abq";
	int ret = strcmp(arr1, arr2);
	printf("%d\n", ret);
	if (ret > 0)
		printf("arr1 > arr2\n");
	else if (ret == 0)
		printf("arr1 == arr2\n");
	else
		printf("arr1 < arr2\n");
	return 0;
}
2、模拟实现
#include<stdio.h>
#include<assert.h>
int my_strcmp(const char* arr1, const char* arr2)
{
	int ret = 0;
	assert(arr1 != NULL);
	assert(arr2 != NULL);
	while (*arr1 == *arr2)
	{
		if (*arr1 == '\0')
			return 0;
		arr1++;
		arr2++;
	}
	return *arr1 - *arr2;
}
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abq";
	int ret = my_strcmp(arr1, arr2);
	if (ret > 0)
		printf("arr1 > arr2\n");
	else if (ret == 0)
		printf("arr1 = arr2\n");
	else
		printf("arr1 < arr2\n");
	return 0;
}

七、strstr函数的使用和模拟实现

函数声明

  • 声明: char * strstr ( const char * str1, const char * str2);

功能

  • strstr 函数,查找 str2 指向的字符串在 str1 指向的字符串中第一次出现的位置。
  • 简而言之:在一个字符串中查找子字符串。
  • strstr 的使用得包含 <string.h>

参数

  • str1:指针,指向了被查找的字符串
  • str2:指针,指向了要查找的字符串

返回值

  • 如果 str1 指向的字符串中存在 str2 指向的字符串,那么返回第一次出现位置的指针
  • 如果 str1 指向的字符串中不存在 str2 指向的字符串,那么返回 NULL
1、代码演示
#include<stdio.h>
#include<string.h>
int main()
{
	char* str1 = "AABCD";
	char* str2 = "AB";
	char* ret = strstr(str1, str2);
	//会找到AB(即相同部分)之后的地址
	printf("%s\n", ret);//会输出ABCD
	//如果没有相同部分的地址就返回NULL
	return 0;
}
2、模拟实现(难点)
#include<stdio.h>
#include<assert.h>
char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);
	char* p = str1;
	char* s1 = str1;
	char* s2 = str2;
	while (*p)
	{
		if (*str2 == '\0')
			return str1;
		s1 = p;
		s2 = str2;
		while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
			return p;
		p++;
	}
	return NULL;
}
int main()
{
	char arr1[] = "abbcdef";
	char arr2[] = "bbc";
	char* p = my_strstr(arr1, arr2);
	if (p != NULL)
		printf("找到了,%s\n", p);
	else
		printf("找不到\n");
	return 0;
}

八、strncpy函数的使用

函数声明

  • 声明: char * strncpy ( char * destination, const char * source, size_t num );

功能

  • 字符串拷贝;将 source 指向的字符串拷贝到 destination 指向的空间中,最多拷贝 num 个字符。

参数

  • destination:指针,指向目的地空间
  • source:指针,指向源头数据
  • num:从 source 指向的字符串中最多拷贝的字符个数

返回值

  • strncpy 函数返回的目标空间的起始地址
1、代码演示
#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[20] = { 0 };
	char arr2[] = "abcdefghi";
	char* str = strncpy(arr1, arr2, 5);
	//将arr2中的前五个字符拷贝到arr1中,如果不够会补\0
	printf("%s\n", arr1);
	printf("%s\n", str);
	return 0;
}
2、模拟实现
#include<stdio.h>
#include<assert.h>
char* my_strncpy(char* dest, const char* src, size_t num)
{
	char* ret =* dest;
	assert(dest != NULL);
	assert(src != NULL);
	while (num)
	{
		*dest = *src;
		dest++;
		src++;
		num--;
	}
	return ret;
}
int main()
{
	char arr1[] = "hello world";
	char arr2[20] = "******************";
	int k = 0;
	scanf("%d", &k);
	my_strncpy(arr2, arr1, k);
	printf("%s", arr2);
	return 0;
}
3、与strcpy对比 
  1. 关于 strcpy
    • 它的作用是进行字符串拷贝,会一直拷贝直到遇到源字符串的结束符 \0
    • 存在的问题是,如果目标空间不足以容纳源字符串,就很容易出现越界行为,可能会导致程序出现不可预料的错误,比如覆盖其他内存区域的数据等。
  2. 关于 strncpy
    • 它指定了拷贝的字符长度,这是它与 strcpy 一个很重要的区别。
    • 源字符串不一定需要以 \0 结束。
    • 由于在使用时需要考虑目标空间的大小是否够用,所以相对 strcpy 函数来说更加安全,能在一定程度上避免缓冲区溢出等问题。

九、strnact函数的使用

函数声明

  • 声明: char * strncat ( char * destination, const char * source, size_t num );

功能

  • 字符串追加;将 source 指向的字符串的内容,追加到 destination 指向的空间,最多追加 num 个字符。

参数

  • destination:指针,指向了目标空间
  • source:指针,指向了源头数据
  • num:最多追加的字符的个数

返回值

  • 返回的是目标空间的起始地址
1、代码演示
#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[] = "hello";
	char arr2[] = " world";
	char* str = strncat(arr1, arr2, 5);
	//我们追加的时候是从第一个\0开始追加的
	//追加完成之后会补\0
	//如果追加到字符数超过了原有字符数
	//只要追加上原来的即可
	printf("%s\n", arr1);
	printf("%s\n", str);
	return 0;
}
2、模拟实现
#include<stdio.h>
#include<assert.h>
char* my_strncat(char* dest, const char* src, size_t num)
{
	char* ret = dest;
	assert(dest != NULL);
	assert(src != NULL);
	while (*dest)
	{
		dest++;
	}
	while (num)
	{
		*dest = *src;
		dest++;
		src++;
		num--;
	}
	return ret;
}
int main()
{
	int k = 0;
	scanf("%d", &k);
	char arr1[20] = "hello ";
	char arr2[] = "world";
	my_strncat(arr1, arr2, k);
	printf("%s", arr1);
	return 0;
}
3、与strcat对比
  • 参数不同strncat 多了一个参数。
  • 追加内容处理strcat 函数在追加的时候要将源字符串的所有内容,包含 \0 都追加过去,但是 strncat 函数指定了追加的长度。
  • 源字符串结束符要求strncat 函数中源字符串中不一定要有 \0 了。
  • 灵活性和安全性strncat 更加灵活,也更加安全。

十、strncmp函数的使用

函数声明

  • 声明: int strncmp ( const char * str1, const char * str2, size_t num );

功能

  • 字符串比较;比较 str1 和 str2 指向的两个字符串的内容,最多比较 num 字符。

参数

  • str1:指针,指向一个比较的字符串
  • str2:指针,指向另外一个比较的字符串
  • num:最多比较的字符个数

返回值

  • 标准规定:
    • 第一个字符串大于第二个字符串,则返回大于 0 的数字
    • 第一个字符串等于第二个字符串,则返回 0
    • 第一个字符串小于第二个字符串,则返回小于 0 的数字
1、代码演示
#include<stdio.h>
#include<string.h>
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abcqw";
	int ret1 = strncmp(arr1, arr2, 3);
	printf("%d\n", ret1);
	int ret2 = strncmp(arr1, arr2, 4);
	printf("%d\n", ret2);
	return 0;
}
2、模拟实现
#include<stdio.h>
#include<assert.h>
int my_strncmp(const char* str1, const char* str2, size_t num)
{
	assert(str1 != NULL);
	assert(str2 != NULL);
	int ret = 0;
	while (num)
	{
		if (*str1 == *str2)
		{
			if (*str1 == 0)
			{
				break;
			}
			str1++;
			str2++;
		}
		else
		{
			ret = *(unsigned char*)str1 - *(unsigned char*)str2;
			break;
		}
		num--;
	}
	return ret;
}
int main()
{
	char arr1[] = "abcd";
	char arr2[] = "abd";
	int k = 0;
	scanf("%d", &k);
	int ret = my_strncmp(arr1, arr2, k);
	if (ret > 0)
		printf("arr1 > arr2");
	else if (ret == 0)
		printf("arr1 = arr2");
	else
		printf("arr1 < arr2");
	return 0;

}
3、与strcmp对比
  1. 参数不同strncmp 比 strcmp 多了一个用于指定最多比较字符个数的参数。
  2. 比较长度方面strncmp 可以比较任意长度的字符串(通过指定 num 参数来控制比较的字符个数)。
  3. 灵活性和安全性strncmp 更加灵活,也更加安全,因为它可以通过指定比较长度来避免一些不必要的问题,例如在处理不确定长度的字符串时可以更好地控制比较过程。

十一、strtok函数的使用

函数声明

  • 声明: char *strtok(char *str, const char *delim);

功能

  • 分割字符串:根据 delim 参数中指定的分隔符,将输入字符串 str 拆分成多个子字符串。
  • 修改原始字符串strtok 会直接在原始字符串中插入 '\0' 终止符,替换分隔符的位置,因此原始字符串会被修改。

参数

  1. str:首次调用时传入待分割的字符串;后续调用传入 NULL,表示继续分割同一个字符串。
  2. delim:包含所有可能分隔符的字符串(每个字符均视为独立的分隔符)。

返回值

  • 成功时返回指向当前子字符串的指针。
  • 没有更多子字符串时返回 NULL

使用步骤

  1. 首次调用:传入待分割字符串和分隔符。
  2. 后续调用:传入 NULL 和相同的分隔符,继续分割。
  3. 结束条件:当返回 NULL 时,表示分割完成。
1、代码演示
#include<stdio.h>
#include<string.h>
int main()
{
	char arr[] = "zpengwei@yeah.net";
	char sep[] = "@.";

	char buf[20] = { 0 };
	strcpy(buf, sep);

	//我们将arr拷贝一份到buf中,修改buf不去动arr
	//第一次调用
	//第一次调用会将zpengwei@yeah.net中的@改为\0
	//即zpengwei\0yeah.net并且返回z的地址
	char* p1 = strtok(buf, sep);
	printf("%s\n", p1);//zpengwei

	//后续调用都传入空指针
	//第二次调用会将zpengwei\0yeah.net中的.改为\0
	//即zpengwei\0yeah\0net并且返回y的地址
	char* p2 = strtok(NULL, sep);
	printf("%s\n", p2);//yeah

	char* p3 = strtok(NULL, sep);
	printf("%s\n", p3);//net

	//如果还有p4则会返回NULL
	char* p4 = strtok(NULL, sep);
	printf("%s\n", p4);//(null)
	return 0;
}

这段代码也可以采取循环的方式去优化

#include<stdio.h>
#include<string.h>
int main()
{
	char arr[] = "zpengwei@yeah.net";
	char sep[] = "@.";

	char buf[200] = { 0 };
	strcpy(buf, arr);

	char* p = NULL;
	for (p = strtok(buf, sep); p != NULL; p = strtok(NULL, sep))
	{
		printf("%s\n", p);
	}
	return 0;
}
2、注意事项

破坏性操作

  • strtok 会直接修改原始字符串,将其中的分隔符替换为 '\0'。如果需要保留原字符串,应先拷贝一份。

连续分隔符

  • 多个连续的分隔符会被视为单个分隔符,不会返回空字符串。

空指针处理

  • 如果输入的 str 为 NULL 且没有前序调用,行为未定义。

十二、strerror函数的使用

函数声明

char* strerror ( int errnum );

功能

  1. strerror 函数可以通过参数部分的 errnum 表示错误码,得到对应的错误信息,并且返回这个错误信息字符串首字符的地址。
  2. strerror 函数只针对标准库中的函数发生错误后设置的错误码的转换。
  3. strerror 的使用需要包含 <string.h>
 

在不同的系统和 C 语言标准库的实现中都规定了一些错误码,一般是放在 <errno.h> 这个头文件中说明的。C 语言程序启动的时候就会使用一个全局的变量 errno 来记录程序的当前错误码,只不过程序启动的时候 errno 是 0,表示没有错误。当我们在使用标准库中的函数的时候发生了某种错误,就会将对应的错误码存放在 errno 中。而一个错误码的数字是整数,很难理解是什么意思,所以每一个错误码都是有对应的错误信息的。strerror 函数就可以将错误码对应的错误信息字符串的地址返回。

参数

  • errnum:表示错误码。
 

这个错误码一般传递的是 errno 这个变量的值,在 C 语言中有一个全局的变量叫 errno,当库函数的调用发生错误的时候,就会将本次错误的错误码存放在 errno 这个变量中。使用这个全局变量需要包含头文件 <errno.h>

返回值

函数返回通过错误码得到的错误信息字符串的首字符的地址。

1、代码演示
#include <errno.h>
#include <string.h>
#include <stdio.h>
//我们打印⼀下0~10这些错误码对应的信息 
int main()
{
 int i = 0;
 for (i = 0; i <= 10; i++) {
 printf("%d: %s\n", i, strerror(i));
 }
 return 0;
}

#include <stdio.h>#include <string.h>
#include <errno.h>
int main ()
{
 FILE * pFile = NULL;
 //fopen函数以读的形式打开⽂件,如果⽂件不存在,则打开失败。 
 pFile = fopen ("unexist.ent", "r");
 if (pFile == NULL)
 {
 printf ("错误信息是:%s\n", strerror(errno));
 return 1;//错误返回 
 }
 
 return 0;
}

2、perror

也可以了解一下 perror 函数, perror 函数相当于一次将上述代码中的第11行完成了,直接将错误 信息打印出来。 perror 函数打印完参数部分的字符串后,再打印一个冒号和一个空格,再打印错误信息。

#include <stdio.h>
#include <string.h>
#include <errno.h>
int main ()
{
 FILE * pFile = NULL;
 pFile = fopen ("unexist.ent", "r");
 if (pFile == NULL)
 {
 perror("错误信息是");
 return 1;
 }
 return 0;
}

评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值