(C语言)字符串函数及模拟实现相应函数

从msdn里可以查找这些库函数的使用及定义,并且,字符串相关的函数在使用时,需要包含头文件:

#include<string.h>

目录

一、求字符串长度的函数——strlen

模拟实现strlen

计数器的方法

指针 - 指针

递归

二、长度不受限制的字符串函数

1、字符串拷贝——strcpy

模拟

2、字符串追加——strcat

 模拟

3、字符串比较——strcmp

模拟

三、长度受限制的字符串函数

1、字符串拷贝——strncpy

模拟

2、字符串追加——strncat

模拟

3、字符串比较——strncmp

模拟

四、字符串查找

1、查找子串——strstr

模拟

2、切割字符串——strtok

 模拟

五、错误信息报告——strerror


一、求字符串长度的函数——strlen

要点:       

        1、字符串将'\0'作为结束标志,strlen函数返回的是在字符串中'\0'前面出现的字符个数(不包含'\0')

        2、参数指向的字符串必须要以'\0'结束

        3、注意函数的返回值是size_t,是无符号的 ,即:unsiged int,转入定义查看:

使用举例:

//使用strlen
int main()
{
	int ret = strlen("abcdef");
	printf("%d\n", ret);
	return 0;
}

输出:6

注:字符串的末尾,系统会自动添加一个'\0' 

错误示范:

int main()
{
	char a[3] = { 'l','y','j' };
	printf("%d\n", strlen(a));
	return 0;
}

思考它的结果会是什么呢?

        我在本次运行时,输出42,但是并不能保证我下一次也是42,为什么呢?

        数组里面是是一个一个存放的字符,后面并没有'\0',strlen就会一直往内存后面去数,直到遇到'\0'为止,因此他的值是没有办法确定的。

        有人就会去做实验,说,我不是每次运行出来都是一样的吗?两个问题,和我的结果一样吗?如果你再往代码加点东西,还一样吗?

如下:

int main()
{
	char a[3] = { 'l','y','j' };
	char b[5] = { "aagga" };
	printf("%d\n", strlen(a));
	return 0;
}

 这个代码运行时,内存里还会存放数组b的内容,下面来看结果:

所以这种写法也没有意义呀,求不出来它的真实值

 下面再来关注strlen使用的一个小细节:

int main()
{
	if (strlen("abc") - strlen("abcdef")>0)
	{
		printf(">\n");
	}
	else
	{
		printf("<\n");
	}
	return 0;
}

 思考:输出什么?

正确输出:>

        原因:因为strlen返回的是size_t,无符号整型,所以两个无符号整型相减还是无符号整型

相减为-3,-3如果当作无符号数时,内存中存放的是它的补码,算出来将会是一个非常大的正数

正确写法:

int main()
{
	if (strlen("abc") >strlen("abcdef"))
	{
		printf(">\n");
	}
	else
	{
		printf("<\n");
	}
	return 0;
}

模拟实现strlen

代码1:

计数器的方法:

#include<stdio.h>
#include<assert.h>
size_t my_strlen(const char* p)
{
	assert(p);
	size_t count = 0;
	while (*p != '\0')
	{
		count++;
		p++;
	}
	return count;
}
int main()
{
	size_t len = my_strlen("abcdef");
	printf("%d\n", len);
	return 0;
}

         像库函数一样,返回值设置为size_t,原因是:计算长度不可能为负数,但是在使用时,注意一下,不要出现上述的bug,当然也可以设置为int,不影响。

        将字符串传过去,实际上传的是首字符的地址,所以函数用字符指针来接收,又考虑到,我们只是数它的长度,并不会去改变指针里面的内容,所以我们用const 来限制他,更加安全。

接收指针,先断言指针是否为空,即利用assert,使用它时,需要包含头文件,<assert.h>

后续一个一个数,遇到'\0'结束即可,利用count计数。

方法二:

指针 - 指针

size_t my_strlen(const char* p)
{
	assert(p);
	char* str = p;
	while (*p != '\0')
	{
		p++;
	}
	return p-str;
}
int main()
{
	size_t len = my_strlen("abcdef");
	printf("%d\n", len);
	return 0;
}

注:

        指针-指针的绝对值得到是之间的元素,所以返回时,注意是尾指针-减去头指针,否则,它返回的是size_t,就会将其负数转化成一个很大的正数,或者你也可以将返回值设为int

方法三:

递归

size_t  my_strlen(const char* p, int count )
{
	if (*p != '\0')
	{
		count++;
		my_strlen(++p, count);
	}
	else
		return count;
}
int main()
{
	size_t len = my_strlen("abcdef",0);
	printf("%d\n", len);
	return 0;
}

二、长度不受限制的字符串函数

1、字符串拷贝——strcpy

 source——>源头,destination——>目的地

        就是把源头指针所指向的空间的数据拷贝到目的地指针所指向的空间里面去

 例:

int main()
{
	char a[10] = { 0 };
	strcpy(a, "name");
	printf("%s", a);
	return 0;
}

输出就是:name

注:

        错误写法:a="name";——>a是数组名,是首元素地址,地址是编号,所以它是一个常量,不能给一个常量赋值吧,所以这种写法是错误的。所以需要使用字符串strcpy 

细节:遇到'\0'拷贝结束(它是将'\0'也一并拷过来的),例:

 错误示范1:

int main()
{
	char a[10] = { 0 };
	char b[3] = { 'l','y','j' };
	strcpy(a, b);
	printf("%s", a);
	return 0;
}

找不到 '\0'的具体位置,运行出错啦!(越界了最后)

错误示范2:

int main()
{
	char* p = "abcdef";
	char arr[3] ="lyj";
	strcpy(p, arr);
	printf("%s\n", p);
}

 这里的p所指向的是常量字符串,是不可被修改的,因此运行会出错的。(目标空间必须可变

 小结:

1、源字符串必须以'\0'结束

2、会将源字符串中的'\0'拷贝到目标空间

3、目标空间必须足够大,以确保能存放源字符串

4、目标空间必须可变

模拟:

char* my_strcpy(char* str1, const char* str2)
{
	assert(str1 && str2);
	char* p = str1;
	do
	{
		*str1 = *str2;
		str1++;
		str2++;
	} while (*str2 != '\0');
	return p;
}
int main()
{
	char a[100] = { 0 };
	char b[] = "abcdef";
	my_strcpy(a, b);
	printf("%s\n", a);
	return 0;
}

优化:

char* my_strcpy(char* str1, const char* str2)
{
	assert(str1 && str2);
	char* p = str1;
	while (*str1++ = *str2++)
		;
	return p;
}
int main()
{
	char a[100] = { 0 };
	char b[] = "abcdef";
	my_strcpy(a, b);
	printf("%s\n", a);
	return 0;
}

        返回的是目的地的指针(模拟模拟,库函数是这样设置的),所以一开始需要用一个p来记录它。 

2、字符串追加——strcat

使用举例: 

int main()
{
	char a[20] = "love ";
	char b[10] = "you";
	strcat(a, b);
	printf("%s\n", a);
	return 0;
}

 输出:

 模拟:

char* my_strcat(char* str1, const char* str2)
{
	assert(str1 && str2);
	char* p = str1;
//找到目标空间末尾
	while (*str1!='\0')
		str1++;
//拷贝
	while (*str1++ = *str2++)
		;
	return p;
}
int main()
{
	char a[20] = "bet ";
	char b[10] = "on me";
	my_strcat(a, b);
	printf("%s", a);
	return 0;
}

小结:

1、源字符串必须以'\0'结束

2、目标空间必须有足够的大,能容纳下源字符串的内容

3、目标空间必须可修改

注:这个函数是不可以字符串自己给自己追加

原因:字符串末尾'\0'被覆盖,找不到'\0’停不下来,代码崩溃

3、字符串比较——strcmp

比较的是ASCII码

标准规定:

1、第一个字符串大于第二个字符串,则返回>0的数

2、第一个字符串小于第二个字符串,则返回<0的数

3、第一个字符串等于第二个字符串,则返回0

 使用:

int main()
{
	char a[20] = "abcde";
	char b[20] = "abcdef";
	int ret =strcmp(a, b);
	if (ret > 0)
		printf(">");
	else if (ret == 0)
		printf("=");
	else
		printf("<");
	return 0;
}

输出:< 

模拟:

int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);
	while (*str1== *str2)
	{
		if (*str1 == '\0')
		{
			return 0;
		}
		str1++;
		str2++;
	}
	if (*str1 > *str2)
		return 1;
	else if (*str1 < *str2)
		return -1;
}
int main()
{
	char a[20] = "lyj";
	char b[20] = "lyj";
	int ret = my_strcmp(a, b);
	if (ret > 0)
		printf(">");
	else if (ret == 0)
		printf("=");
	else
		printf("<");
	return 0;
}

优化:

int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2);
	while (*str1 == *str2)
	{
		if (*str1 == '\0')
		{
			return 0;
		}
		str1++;
		str2++;
	}
	return *str1 - *str2;
}

三、长度受限制的字符串函数

        下面三个函数都是比上述的长度不受限制的字符串函数多了一个参数size_t count,来限制长度

1、字符串拷贝——strncpy

 使用:

int main()
{
	char a[10] = { 0 };
	strncpy(a, "name",2);
	printf("%s", a);
	return 0;
}

如果源字符串的长度小于需要拷贝的长度,则会用'\0'来填补

模拟:

char* my_strncpy(char* str1, const char* str2,size_t count)
{
	assert(str1 && str2);
	char* p = str1;
	int n = 0;//标记个数
	while (*str1++ = *str2++)
	{
		n++;
		if (count == n)
			break;
	}
	while(count > n)
	{
		*str1++ = '\0';
		n++;
		if (n == count)
			break;
	}
	return p;
}
int main()
{
	char a[100] = { 0 };
	char b[] = "abcdef";
	my_strncpy(a, b,7);
	printf("%s\n", a);
	return 0;
}

2、字符串追加——strncat

使用:

模拟:

char* my_strncat(char* str1, const char* str2,size_t count)
{
	assert(str1 && str2);
	char* p = str1;
	int n = 0;//标记个数
	while (*str1!='\0')
		str1++;
	while (*str1++ = *str2++)
	{
		n++;
		if (count == n)
				break;
	}
	while(count > n)
	{
		*str1++ = '\0';
		n++;
		if (n == count)
			break;
	}
	return p;
}
int main()
{
	char a[20] = "bet ";
	char b[10] = "on me";
	my_strncat(a, b,4);
	printf("%s", a);
	return 0;
}

3、字符串比较——strncmp

使用:

int main()
{
	char a[20] = "abcde";
	char b[20] = "abcdef";
	int ret =strncmp(a, b,3);
	if (ret > 0)
		printf(">");
	else if (ret == 0)
		printf("=");
	else
		printf("<");
	return 0;
}

输出:=

模拟:

int my_strncmp(const char* str1, const char* str2,size_t count)
{
	assert(str1 && str2);
	int n = 1;//标记
	while (*str1 == *str2)
	{
		if (n == count)
		{
			break;
		}
		else if (*str1 == '\0')
		{
			return 0;
		}
		str1++;
		str2++;
		n++;
	}
	return *str1 - *str2;
}
int main()
{
	char a[20] = "ly";
	char b[20] = "lyj";
	int ret = my_strncmp(a, b,2);
	if (ret > 0)
		printf(">");
	else if (ret == 0)
		printf("=");
	else
		printf("<");
	return 0;
}

输出:= 

四、字符串查找

1、查找子串——strstr

看文档;

        该函数返回的是子串在字符串里面的起始位置,如果没找到子串,则返回空指针

使用: 

int main()
{
	char a[20] = "abcdelyjhaha";
	char b[20] = "lyj";
	char* ret = strstr(a, b);
	if (ret == NULL)
	{
		printf("子串不存在\n");
	}
	else
	{
		printf("%s\n", ret);
	}
	return 0;
}

输出: 

模拟:

char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);
	const char* s1 = str1;
	const char* s2 = str2;
	const char* p = str1;
	while (*p)
	{
		s1 = p;
		s2 = str2;
		while (*s1 != '\0'&&*s2!='\0'&&*s1==*s2)
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
		{
			return (char*)p;
		}
		p++;
	}
	return NULL;
}
int main()
{
	char a[20] = "abcdelyjhaha";
	char b[20] = "lyj";
	char* ret = my_strstr(a, b);
	if (ret == NULL)
	{
		printf("子串不存在\n");
	}
	else
	{
		printf("%s\n", ret);
	}
	return 0;
}

 分析:

2、切割字符串——strtok

        1、Delimit参数是个字符串,定义了用作分割符的字符集合

        2、第一个参数指定一个字符串,它包含了0个或多个由Delimit字符串中一个或多个分隔符分割的标记。

        3、strtok函数找到strToken中的下一个标记,并将其用'\0'结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改)

        4、strtok函数的第一个参数不为NULL,函数将找到strToken中第一个标记,strtok函数将保存他在字符串中的位置

        5、strtok函数的第一个参数为NULL,函数将在同一个字符串中保存的位置开始,查找下一个标记

很绕的样子,使用一下,就明白了

例:

int main()
{
	char a[30] = "lyj@xian.wenlixuyan&ruanjian";
	const char* b = "@.&";//Delimit参数是个字符串,定义了用作分割符的字符集合
	char tmp[30] = { 0 };
	strcpy(tmp, a);//3\strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改

	//strtok函数的第一个参数不为NULL,函数将找到strToken中第一个标记,strtok函数将保存他在字符串中的位置
	char* ret=strtok(tmp, b);
	printf("%s\n", ret);

	//strtok函数的第一个参数为NULL,函数将在同一个字符串中保存的位置开始,查找下一个标记
	ret = strtok(NULL, b);
	printf("%s\n", ret);

	ret = strtok(NULL, b);
	printf("%s\n", ret);

	ret = strtok(NULL, b);
	printf("%s\n", ret);
	
	return 0;

}

优化: 

int main()
{
	char a[30] = "lyj@xian.wenlixuyan&ruanjian";
	const char* b= "@. & ";
	char tmp[30] = { 0 };
	strcpy(tmp, a);
	char* ret = NULL;
	for (ret = strtok(tmp, b); ret != NULL; ret = strtok(NULL, b))
	{
		printf("%s\n", ret);
	}
	return 0;
}

输出:

  

 模拟:

char* my_strtok(char* str1, const char* str2)
{
	assert(str2);
	static int sz1 = NULL;
	static int count = NULL;
	static char* s1 = NULL;
	static char* s2 = NULL;
	int sz = 0;
	if (str1 != NULL)//第一次进入。
	{
		sz1 = strlen(str1);//计算出str1中所有字符的个数
		s2 = str1;//记录初始地址,等下找到分割符时,将这个地址返回。
		sz = strlen(str2);
		for (*str1; *str1 != 0; str1++)
		{
			for (int i = 0; i < sz; i++)
			{
				if (i == 0)
				{
					count++;
				}
				if (*str1 == *(str2 + i))
				{
					*str1 = 0;
					s1 = str1;//记录这一次置0的位置。
					return s2;
				}
			}
		}
	}
	else
	{
		s2 = s1 + 1;
		str1 = s2;
		sz = strlen(str2);
		for (*str1; *str1 != 0; str1++)
		{
			for (int i = 0; i < sz; i++)
			{
				if (i == 0)
				{
					count++;
				}
				if (*str1 == *(str2 + i))
				{
					*str1 = 0;
					s1 = str1;//记录这一次置0的位置。
					return s2;
				}
			}
		}
		if (count > sz1)
		{
			return NULL;
		}
		return s2;
	}

}
int main()
{
	char arr[30] = "lyj@xian.wenlixuyan&ruanjian";
	char* p = "@.&";
	char* str = NULL;
	for (str = my_strtok(arr, p); str != NULL; str = my_strtok(NULL, p))
	{
		printf("%s\n", str);
	}

	return 0;
}

五、错误信息报告——strerror

C语言的库函数,在执行失败的时候,都会设置错误码

如下: 

 (括号里面的错误码,C语言的库函数,在执行失败的时候,都会自动设置相应的错误码)

例如:

结束啦!后面还会出一期关于内存操作的函数,就可以不止是拷贝复制字符串啦!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: C语言中常用的字符串替换函数是`str_replace`,它可以在一个字符串中查找并替换指定的子字符串。下面我将简单地模拟实现一个`str_replace`函数。 ```c #include <stdio.h> #include <stdlib.h> #include <string.h> char* str_replace(const char* str, const char* search, const char* replace) { char* result; int search_len = strlen(search); int replace_len = strlen(replace); int count = 0; // 统计需要替换的次数 const char* s = str; while ((s = strstr(s, search)) != NULL) { count++; s += search_len; } // 计算替换后的字符串长度 int result_len = strlen(str) + count * (replace_len - search_len); result = (char*)malloc(result_len + 1); // 开始替换 char* p = result; s = str; while ((s = strstr(s, search)) != NULL) { int n = s - str; strncpy(p, str, n); p += n; strncpy(p, replace, replace_len); p += replace_len; str = s + search_len; } strcpy(p, str); return result; } int main(void) { const char* str = "Hello, world!"; const char* search = "world"; const char* replace = "everyone"; char* result = str_replace(str, search, replace); printf("替换前: %s\n", str); printf("替换后: %s\n", result); free(result); return 0; } ``` 上述代码实现了一个简单的`str_replace`函数。它首先统计了需要替换的次数,并根据替换的次数计算了替换后的字符串长度。然后,它分配了足够的内存空间来存储替换后的字符串,并进行了替换操作。最后,它返回了替换后的字符串。在`main`函数中,我们可以看到如何使用这个函数来替换一个字符串中的子字符串。 需要注意的是,本示例只是对`str_replace`函数的简单模拟实现,实际使用中还需要处理更多的边界情况和错误处理。 ### 回答2: C语言中没有内置的字符串替换函数,但我们可以通过模拟实现一个字符串替换函数。 首先,我们可以定义一个函数,该函数接受三个参数:源字符串、待替换的子字符串、替换后的子字符串函数的返回值是替换完成后的字符串。 接下来,我们可以使用循环来遍历源字符串。在每次循环中,比较源字符串中是否存在待替换的子字符串。如果存在,我们就将替换后的子字符串复制到新的字符串中。如果不存在,我们将源字符串中的当前字符复制到新的字符串中。 最后,返回新的字符串即可。 下面是一个简单的模拟实现示例: ```c #include <stdio.h> #include <string.h> char* stringReplace(char* source, const char* search, const char* replace) { char* result; int i, j, sourceLen, searchLen, replaceLen, count; sourceLen = strlen(source); searchLen = strlen(search); replaceLen = strlen(replace); count = 0; for (i = 0; i < sourceLen; i++) { if (strstr(&source[i], search) == &source[i]) { count++; i += searchLen - 1; } } result = (char*)malloc(sourceLen + count * (replaceLen - searchLen) + 1); i = 0; j = 0; while (source[i]) { if (strstr(&source[i], search) == &source[i]) { strcpy(&result[j], replace); j += replaceLen; i += searchLen; } else result[j++] = source[i++]; } result[j] = '\0'; return result; } int main() { char source[] = "Hello, World!"; const char search[] = "World"; const char replace[] = "C Language"; char* result = stringReplace(source, search, replace); printf("替换后的字符串: %s\n", result); free(result); return 0; } ``` 这是一个简单的模拟实现,实际上字符串替换还涉及到更多复杂的情况和细节,比如大小写敏感、替换次数限制等等。需要根据实际需求进行更进一步的完善。 ### 回答3: C语言字符串替换函数模拟实现的方法有很多,以下是一种可能的实现方式: ```c #include <stdio.h> #include <string.h> void str_replace(char *str, const char *find, const char *replace) { int find_len = strlen(find); int replace_len = strlen(replace); int str_len = strlen(str); char result[100]; int result_len = 0; int i = 0; while (i < str_len) { if (strncmp(&str[i], find, find_len) == 0) { // 找到需要替换的字符串 strncpy(&result[result_len], replace, replace_len); result_len += replace_len; // 跳过被替换的部分 i += find_len; } else { // 将原字符串的字符复制到结果字符串中 result[result_len] = str[i]; result_len++; i++; } } // 将新的结果字符串复制回原字符串中 strncpy(str, result, result_len); str[result_len] = '\0'; } int main() { char str[100] = "Hello, World!"; char find[10] = "World"; char replace[10] = "Alice"; printf("Before replace: %s\n", str); str_replace(str, find, replace); printf("After replace: %s\n", str); } ``` 这个函数的思路是首先计算出原字符串、需要查找的字符串以及替换的字符串的长度。然后以原字符串为基础,通过遍历每个字符的方式,查找需要替换的字符串,然后将替换的字符串复制到结果字符串中,同时跳过原字符串中已经被替换的部分。最后将结果字符串复制回原字符串中,完成字符串的替换。在主函数中,我们可以看到使用这个函数对原始字符串中的特定字符串进行了替换操作。运行程序后,可以输出替换后的字符串
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

龙洋静

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值