字符串函数的使用及模拟实现:strtok&strstr&strerror:

2023-11-19

字符串函数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 ofstr1.

在一个字符串arr1里面找另外一个字符串arr2,如果能找到的话,一定是第一次出现arr2的位置,则会返回第一次出现arr2字符串的首元素的地址,比如:

 字符串arr2是"bcd" ,,已知字符串arr1里面两个"bcd"字符串,即包含两个字符串arr2,,所以能够找到,要注意找到的是第一次出现arr2的位置,并且返回第一次出现arr2字符串首元素的地址,即返回的是字符串arr1的第二个元素,字符b的地址。

 但是,如果arr2是"bcdq",,在arr1里面找不到,,那么该函数就会返回一个空指针NULL;

字符串函数strstr的模拟实现:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
#include<assert.h>

//字符串函数strstr的使用
//int main()
//{
//	char arr1[] = "abcdefabcdef";
//	char arr2[] = "bcd";
//	//在arr1里面查找是否包含arr2数组
//	char* ret= strstr(arr1, arr2);
//	if (ret == NULL)
//	{
//		printf("找不到\n");
//	}
//	else
//	{
//		printf("找到了:%s\n", ret);
//	}
//	return 0;
//}


//字符串函数strstr的模拟实现
char* my_strstr(const char* str1, const char* str2)//在arr1里面找arr2,只进行查找,不改变两者的内容
{
	assert(str1 && str2);
	//为了保证还能倒退回来,让s1,s2去移动,str1,str2同来记录一下开始的位置
	const char* s1 = str1;
	const char* s2 = str2;
	//一旦匹配失败,能够让s1返回到首字母开始匹配的位置
	const char* cp = str1;

	//该函数有一些特殊的规定,对空字符串有特殊规定
	if (*str2 == '\0')
	{
		return (char*)str1;
		//调用函数形参部分中的str1类型是const char* ,,而返回类型是char*,现在,return str1,是返回值,两者类型不一样,由于现在需要返回,
		//return str1 中的str1类型是const char*,,而返回类型是char*,为了保持一致,要强制类型转换。
	}
	while (*cp)
	{
		s1 = cp;
		s2 = str2;//这两行代表着匹配失败能够返回到预定的位置
		while (*s1 && *s2 && (*s1 == *s2))//如果相等,同时++,再看下一个字符是否一样,防止越界访问
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')
		{
			return (char*)cp;//定义cp的类型为const char * ,,而返回值类型是char*,为了保持一致要强制类型转换
		}
		//如果不一样,s2保持不动,让cp++,再把cp的值赋给s1
		cp++;
	}
	return NULL;
}
int main()
{
	char arr1[] = "abbbcdef";
	char arr2[] = "bbcq";
	//在arr1里面查找是否包含arr2数组
	char* ret = my_strstr(arr1, arr2);
	if (ret == NULL)
	{
		printf("找不到\n");
	}
	else
	{
		printf("找到了:%s\n", ret);
	}
	return 0;
}

在本题中,也可以写成:

char *cp = (char *) str1;

char *cp = (char *) str2;

char *cp = (char *) str1;

除此之外,*str2=='\0' 和 *s2=='\0' 都可以改写成 !*str2 和 !*s2

这是因为,!逻辑取反的优先级高于*,但是,!和*之间没有数据,即使!的优先级高于*,仍要先进行解引用,然后整体再进行逻辑取反。

即:

strtok字符串函数的使用:

作用:切割一个字符串,比如,给一个字符串, "abc@def.cn" 其中,@和. 都是分隔符,,如果想得到三个部分,即 abc def cn ,,这就可以使用strtok字符串函数进行分隔,则使用分隔符来切割该字符串,该函数介绍如下:

1.sep参数是个字符串,定义了用作分隔符的字符集合

比如,切割字符串 "abc@def.cn",,其中,分隔符有@和. 那么此处的sep定义了分隔符的字符集合,则sep包含 @和.

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

此处所指的第一个参数就是要被分隔的字符串,此处的标记,即指由分隔符分隔开的各个部分,在此即指abc def cn这三个部分,如果被切割的字符串是一个空字符串,即,一个字符串内只有末尾的\0,,就是空字符串,表示为:"",,那么此时的标记个数即为0个。

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

strtok函数先找到一个标记,比如,如果分隔字符串"abc@def.cn",那么strtok函数就会先找到第一个标记abc,然后将其\0结尾,即指把第一个分隔符@更改成\0,再返回指向第一个标记的指针,即返回指向标记abc的指针,也就是字符a的地址,然后再以%s打印的话,就会把字符串abc打印出来,因为此时的被分割的原字符串即为:"abc\0def.cn",要注意的是,该字符串函数会把原字符串改变掉,所以,在切割原字符串之前,需要对原字符串做一个临时拷贝并可修改操作,所以要新创建一个字符串,把原字符串内容拷贝到新字符串里面,然后对新字符串进行切割,就不会影响原字符串的内容了,所以可以使用strcpy函数先对原字符串进行一份临时拷贝即可。

4.strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。

调用一次strtok函数就只能找到一个标记,如果我们的被分割的字符串中有多个标记,则就需要调用多个strtok函数来找到对应的标记,此处的strtok函数将保存它在字符串中的位置即指,会记录第一个标记后@被改为\0后的\0的地址。

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

如果第一个标记后的分隔符被改为\0,就会被记录下来,如果下一次使用strtok函数的第一个参数传NULL进去,就会在同一个字符串中被保存的位置,即上一次strtok函数中的分隔符被改为\0后的\0的位置,并从该位置查找下一个标记,比如切割字符串:"abc@def.cn",,第一次strtok函数会把@改成\0,此时\0的位置会被记录,如果下一次调用函数strtok,就会从该\0的位置往后查找下一个标记,所以,就会把下一个点分隔符改成\0,,同时返回这一个标记的地址,即首元素的地址,即字符d的地址,并且会记录下来第二个\0的位置,此时tmp字符串的内容就变成了:"abc\0def\0cn",,还要注意的是,我们在程序中看到的\0会被系统认为是一个整体,即转义字符,,但是,如果当我们在输入的时候输进去\0,系统不会认为是一个整体,会被认为是两个字符,即字符\和字符0,,这一点要注意,特别是当使用scanf函数进行输入的时候,要注意。

6.如果字符串中不存在更多的标记,则返回 NULL 指针。

当标记全部被查找完毕的时候,如果你再调用strtok函数,系统还会继续帮你找,但是找不到了,就会返回NULL。

所以,strtok函数,在调用的时候,只有第一次调用需要把tmp字符串的首元素传过去,下面的strtok函数调用只需要在第一个参数上传NULL即可,由此我们发现,如果一个被分割的字符串中存在多个标记的话,,就需要调用多次strtok函数,代码就会冗余,所以我们可以写成for循环的形式,,由于for循环的第一个参数是初始化部分,仅被执行一次,恰好我们第一个strtok函数调用的第一个参数和后面的strtok函数调用的第一个参数不同,整符合要求,所以,写成for循环更好。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
//strtok函数的使用:

int main()
{
	char arr[] = "abc@def.cn hello";//被分割的字符串
	const char* p = "@. "; //sep分隔符集合,可以乱序,空格字符也可以当做分隔符;
	//不可以使用 \0 分隔,因为在程序中 \0 是字符串结束的标志,会被系统认为是转义字符,会结束;
	char tmp[50] = { 0 };//拷贝原字符串放在tmp中,对tmp进行操作即可,从而不会改变原字符串中的内容,要保证有足够大的空间来放源字符串;
	strcpy(tmp, arr);
	// tmp字符串中的内容:   abc\0def\0cn\0	

	char* ret=strtok(tmp, p);
	printf("%s\n", ret);
	ret=strtok(NULL, p);
	printf("%s\n", ret);
	ret=strtok(NULL, p);
	printf("%s\n", ret);
	return 0; 
}

此种方法存在缺陷,当标记比较多的时间代码就会冗余,因此要优化代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
//strtok函数的使用:

int main()
{
	//char arr[] = "abc@def.cn hello";//被分割的字符串
	//const char* p = "@. "; //sep分隔符集合,可以乱序,空格字符也可以当做分隔符;
	不可以使用 \0 分隔,因为在程序中 \0 是字符串结束的标志,会被系统认为是转义字符,会结束;
	//char tmp[50] = { 0 };//拷贝原字符串放在tmp中,对tmp进行操作即可,从而不会改变原字符串中的内容,要保证有足够大的空间来放源字符串;
	//strcpy(tmp, arr);
	 tmp字符串中的内容:   abc\0def\0cn\0	

	//char* ret=strtok(tmp, p);
	//printf("%s\n", ret);
	//ret=strtok(NULL, p);
	//printf("%s\n", ret);
	//ret=strtok(NULL, p);
	//printf("%s\n", ret);

	//优化代码
	/*for (char* ret = strtok(tmp, p); ret != NULL; ret = strtok(NULL, p))
	{
		printf("%s\n", ret);
	}  */

	//优化代码
	char* ret = strtok(tmp, p);
	while (ret != NULL)
	{
		printf("%s\n", ret);
		ret = strtok(NULL, p);
	}

	return 0; 
}

大胆推测一下,我们知道该strtok函数在运行过程中会有记忆功能,但是,对于局部变量来说的话,他出了自己的作用域就会被销毁,不存在记忆功能,而现在出现的记忆功能也就说明,在该函数的实现过程中,应该是在局部变量前面加static变成静态变量或者是全局变量得到的记忆功能,当静态变量和全局变量都可以满足要求的时候,我们应该优先选取静态变量,不选取全局变量。

下面给出两个关于字符串函数strtok函数使用的例题:

/* strtok example */
#include <stdio.h>
#include <string.h>
int main ()
{
  char str[] ="- This, a sample string.";
  char * pch;
  printf ("Splitting string \"%s\" into tokens:\n",str);
  pch = strtok (str," ,.-");
  while (pch != NULL)
 {
    printf ("%s\n",pch);
    pch = strtok (NULL, " ,.-");
 }
  return 0; }
#include <stdio.h>
int main()
{
   char *p = "zhangpengwei@bitedu.tech";
 const char* sep = ".@";
 char arr[30];
 char *str = NULL;
 strcpy(arr, p);//将数据拷贝一份,处理arr数组的内容
 for(str=strtok(arr, sep); str != NULL; str=strtok(NULL, sep))
 {
 printf("%s\n", str);
 }
}

strerror字符串函数的使用:

在使用一些库函数的时候,可能会存在失败,在调用库函数失败的时候,都会设置错误码, int errno,,,只要调用库函数发生错误时,就会把错误码放在变量errno中,错误码我们不知道它的意思,,此时就可以使用strerror函数来把错误码翻译成错误原因;

 errnum就是错误码,当把错误码转换为错误信息的时候,错误信息是一个字符串,所以返回的就是该字符串首元素的地址,即类型为char*,然后使用%s进行打印,就可以把错误信息打印出来,返回错误码,所对应的错误信息。

使用FILE 需要引头文件#include,使用errno则需要引头文件#include,,系统会把错误码放在变量errno中,然后使用字符串函数strerror来把错误码翻译成错误信息,要知道,错误信息是一个字符串,并且会把错误信息的首元素的地址返回,然后再以%s进行打印,就可以把错误信息打印出来,字符串函数strerror需要引用头文件#include<string.h>;

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<string.h>
//strerror函数的使用:
//int errno;
//#include<errno.h>
//int main()
//{
//
//	FILE* pf = fopen("text.txt", "r");
//	//打开文件函数,r代表只读;
//	//如果该文件不存在话,就会打开失败,失败的话,pf里面存的就是NULL空指针,就会把错误码放在变量errno里面;
//	if (pf == NULL)
//	{
//		printf("%s\n", strerror(errno));
//		//errno是一个全局错误码,即,全局变量,要使用的话,需要包含以下头文件;
//		return 0;//main函数的返回类型是int整型,只要返回值和该类型匹配即可。
//	}
//	//成功
//	//....
//	fclose(pf);
//	pf = NULL;
//	return 0;
//}


今天就分享到此,谢谢大家!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

字符串函数的使用及模拟实现:strtok&strstr&strerror: 的相关文章

随机推荐