栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C语言 一些字符串库函数和其模拟实现

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C语言 一些字符串库函数和其模拟实现

目录
  • 字符串函数(下列头文件均为“string.h”)
    • 1.==strlen== (求字符串长度)
      • 原型
      • 模拟实现
    • 2.==strcpy== (字符串拷贝)
      • 原型
      • 模拟实现
    • 3.==strcat==(字符串拼接)
      • 原型
      • 模拟实现
    • 4.==strcmp==(字符串比较)
      • 原型
      • 模拟实现
    • 5.strncpy(长度受限字符串拷贝)
      • 原型
    • 6.strncat(长度受限字符串拼接)
      • 原型
    • 7.strncmp(长度受限字符串比较)
      • 原型
    • 8.==strstr==(字符串子串查找)
      • 原型
      • 应用
      • 模拟实现
    • 9.==strtok==(字符串分割)
      • 原型
      • 应用
    • 10.strerror(错误报告
      • 原型
      • 应用
  • 字符分类函数
  • 内存操作函数(下列头文件均为“string.h”)
    • 1.==memcpy==(按字节内存拷贝)
      • 原型
      • 模拟实现+特殊应用
    • 2.==memmove==( 可按字节拷贝两个重叠内存)
      • 原型
      • 模拟实现+特殊应用
    • 3.==memset==(按字节内存赋值)
      • 原型
      • 应用
    • 4.memcmp(比较内存内容大小)
      • 原型

字符串函数(下列头文件均为“string.h”) 1.strlen (求字符串长度) 原型

  • 字符串计数不需修改字符串,因此用conut修饰;
模拟实现
#include
#include
#include
#pragma warning(disable:4996)

//方法1:计数器法--需要单独设置一个变量
size_t myStrlen1(const char *arr)  //字符串不需修改字符串,因此用conut修饰
//size_t在vs2013指无符号整型unsigned int
{
	assert(arr); //assert函数用来判断字符串是否不为空

	int count = 0;
	while (*arr)
	{
		count++;
		arr++;
	}
	return count;
}

//方法2:递归法--不创建临时变量
size_t myStrlen2(const char *arr)
{
	assert(arr);

	if (*arr == '')
	{
		return 0;
	}
	else
	{
		return 1 + myStrlen2(arr + 1);
	}
}

//方法3:指针法--利用尾指针-头指针获得长度
size_t myStrlen3(const char *arr)
{
	assert(arr); 

	char *p = arr;
	while (*p != '')
	{
		p++;
	}
	return p - arr;
}

int main()
{
	const char *arr = "abcd1234";
	int len1 = myStrlen1(arr);
	printf("方法1字符串arr长度为:%dn", len1);
	int len2 = myStrlen2(arr);
	printf("方法2字符串arr长度为:%dn", len1);
	int len3 = myStrlen3(arr);
	printf("方法3字符串arr长度为:%dn", len1);

	system("pause");
	return 0;
}

2.strcpy (字符串拷贝) 原型

  • 将后面的参数拷贝到前面的参数,无需修改后者,因此后面参数用const修饰;
  • 注意后面的字符串参数大小不能大于前者,否则会报错;
  • 该函数返回值为char *类型,这是为了支持该类库函数的链式调用;
模拟实现
#include
#include
#include
#pragma warning(disable:4996)

//编程习惯之-变量名:ret->result(结果);src->source(源数据);dest->destination(目标)

char *myStrcpy(char *dest, const char *src)
{
	assert(src);  assert函数用来判断字符串是否不为空
	assert(dest);

	char * ret = dest;
	while ((*dest++ = *src++));
	return ret;
}

int main()
{
	const char *src = "abcd";
	char dest[30] = { "12345" };
	printf("before:%sn",dest);
	printf("after:%sn", myStrcpy(dest, src));

	system("pause");
	return 0;
}

3.strcat(字符串拼接) 原型

  • 将后面的参数拼接到前面的参数,无需修改后者,因此后面参数用const修饰;
  • 前者字符串空间须足够大以容纳拼接后字符串;
  • 注意不能进行自我拼接(这样拼接时会出现原始串不断增加的情况);
  • 该函数返回值为char *类型,这是为了支持该类库函数的链式调用;
模拟实现
#include
#include
#include
#pragma warning(disable:4996)

char *myStrcpy(char *dest, const char *src)
{
	assert(src);
	assert(dest);

	char * ret = dest;
	while (*dest) //将指针指向dest字符串尾部
	{
		dest++;
	}
	while ((*dest++ = *src++)); //从尾部开始将后者字符串拼接
	return ret;
}

int main()
{
	const char *src = "abcd";
	char dest[30] = { "12345" };
	printf("before:%sn", dest);
	printf("after:%sn", myStrcpy(dest, src));

	system("pause");
	return 0;
}

4.strcmp(字符串比较) 原型

  • 两字符串进行大小比较,无需修改字符串,因此两参数用const修饰;
  • 字符串比较是通过两字符串第一个不同的字符的ASSIC码值来判断;
  • 若str1>str2,返回1;若str1=str2,返回0;若str1
模拟实现
#include
#include
#include
#pragma warning(disable:4996)

int myStrcmp(const char *str1, const char *str2)
{
	int ret = 0;
	assert(str1);
	assert(str2);

	//两字符串指针相减,若相同则看下一个字符,直到出现大小不同的字符或''。
	while (!(ret = *(unsigned char *)str1 - *(unsigned char *)str2) && *str2)  
	{
		++str1, ++str2;
	}
	if (ret < 0)
	{
		ret = -1;
	}
	else if (ret>0)
	{
		ret = 1;
	}
	return (ret);
}

int main()
{
	const char *str1 = "abcd4";
	const char *str2 = "abcd5";

	int comp = myStrcmp(str1, str2);
	if (comp > 0)
	{
		printf("str1>str2n");
	}
	else if (comp < 0)
	{
		printf("str1 

5.strncpy(长度受限字符串拷贝) 原型

  • “安全拷贝”函数,即相比strcpy函数来说,参数增加了需要拷贝后者字符串的长度参数;
  • 拷贝后不会自带’’,需要自己加;

6.strncat(长度受限字符串拼接) 原型

  • “安全拼接”函数,相比strcat函数来说,参数增加了需要拼接后者字符串的长度参数;
  • 默认会在拼接后的字符串加’’;

7.strncmp(长度受限字符串比较) 原型

  • “安全比较”函数,比较两字符串的前num个字符;
  • 运算规则同strcmp;

8.strstr(字符串子串查找) 原型

  • 该函数能返回str1里第一次出现str2函数的第一个字符地址;
应用
  • 替换一段字符串的某一个字串
  • 通过while循环实现查找一段字符串中某字符串出现的位置即次数;
    (以下为两个strtr的应用举例)
//strstr example_1 替换
//将一个长字符串的一个小字符串替换
int main()
{
	char str[] = "This is a simple string.";
	char *pch;
	pch = strstr(str, "simple");
	strncpy(pch, "sample", 6);
	puts(str);

	system("pause");
	return 0;
}

//strstr example_2 查找
//从一个长字符串找到小字符串的具体位置
int main()
{
	const char *s1 = "abcd1234worldABCD-1world,nkworld.";
	const char *s2 = "world";

	const char *s = s1;
	int count = 0;
	char *sub_str = NULL;
	while (1)
	{
		sub_str = strstr(s, s2);
		if (sub_str == NULL)
		{
			break;
		}
		count++;
		//sub_str = strcpy(sub_str, s2);
		printf("第%d个%s的位置:%dn", count, s2, sub_str - s1);
		s = sub_str + 1;
	}

	system("pause");
	return 0;
}

模拟实现
char *myStrstr(const char*str1, const char *str2)
{
	assert(str1);
	assert(str2);

	char* str_p = (char*)str1;
	char* str_q = (char*)str2;

	char* judge_p = str_p; //外部循环瞄点

	if (*str2 == '')
	{
		return NULL;
	}
	while (*judge_p!='')
	{
		const char* move_p = judge_p ; //内部循环str1比较指针
		char* judge_q = str_q; //内部循环str2比较指针
		while (*move_p && *judge_q && *move_p == *judge_q)
		{
			move_p++;
			judge_q++;
		}
		if (*judge_q == '')
		{
			return judge_p;
		}
		judge_p++;
	}
	return NULL;
}

int main()
{
	const char *s1 = "abcd1234world5678";
	const char *s2 = "world";

	char *s = myStrstr(s1, s2);
	printf("before:%sn", s1);
	printf("after:%sn", s);

	system("pause");
	return 0;
}


9.strtok(字符串分割) 原型

  • 分割字符串函数:遍历参数一所指向的字符串,遇到参数二所包含的分割字符后即返回对应字符串首指针;
  • 第二个参数可存放多个分割字符,比如:",._ "(双引号内的字符都是,包括空格);
  • 一次使用只能返回一个分割字串,因为只有一个返回值;
  • 即如果要多次对一个字符串使用该函数,第一次使用后下一次传参只能将参数一设为NULL,否则将清空记录,从新对该字符串起始位置遍历
  • strtok库函数内部有static变量保存原字符串地址作为静态全局变量,当分割一个后,自动移向第一个分隔符后一个地址
  • 分割字符串本质是将原字符串的对应分隔字符转为’’;
  • 【注意】由上个条例可知该库函数会修改参数一,故参数一所指向的字符串不能定义在字符常量区;
应用
  • 通过多次引用该函数将一个长字符串分割为多个小字符串;
  • 注意:由上面的特性4,5可知,重复调用函数以分割多个子字符串时,第一个参数不用变;
//strtok example_1
//将一个长字符串分割为多个小字符串

int main()
{
	char s1[] = "You are so beautiful,but not belong to me.";
	char *s = strtok(s1, ", .");
	while (s != NULL)
	{
		printf("%sn", s);
		s = strtok(NULL, ", .");
	}

	system("pause");
	return 0;
}


10.strerror(错误报告 原型

  • 通过错误码返回错误信息;
  • 错误码的理解:Eg:return 0;中的0就是main函数返回的一个“错误码”,一般0代表success;
  • 错误码的存在方便计算机,错误信息方便程序员;计算机擅长处理数字但不擅长处理字符串,与人相反;
  • C语言系统全局变量 errno ,在使用C库函数出错时,会自动设置相应错误信息的错误码;
应用
#include
#include
#include
#include
#pragma warning(disable:4996)

//strerror example
//通过打开错误文件了解错误信息与错误码
//使用系统全局变量errno必须加头文件
int main()
{
	printf("before errno:%dn", errno); //输出errno原始内容
	FILE * pf = NULL;
	pf = fopen("test.text", "r");
	if (pf == NULL)
	{
		printf("after errno:%dn", errno); //输出errno遇到错误后自动修改的内容
		printf("ERROR Opening file test.text:%sn", strerror(errno));
	}

	system("pause");
	return 0;
}


字符分类函数

字符分类函数函数(若其参数符合下列条件就返回真) 头文件:
iscntrl :任何控制字符
isspace :空白字符:空格‘ ’,换页‘f’,换行’n’,回车‘r’,制表符’t’或者垂直制表符’v’
isdigit :十进制数字 0~9
isxdigit :十六进制数字,包括所有十进制数字,小写字母af,大写字母AF
islower :小写字母a~z
isupper :大写字母A~Z
isalpha :字母az或AZ
isalnum :字母或者数字,az,AZ,0~9
ispunct :标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph :任何图形字符
isprint :任何可打印字符,包括图形字符和空白字符


内存操作函数(下列头文件均为“string.h”)
  • mem系列库函数的操作基本单位是字节
  • 参数类型为void *表明可以对任何类型进行该操作
1.memcpy(按字节内存拷贝) 原型

  • 不能进行自我拷贝,这也是与memmove的区别所在;(但现在的编辑器已支持,意味着两函数没有区别)
模拟实现+特殊应用
#include
#include
#include
#include
#pragma warning(disable:4996)
//模拟实现memcpy
//test2与test3为特殊应用
void* myMemcpy(void* dest, const void* src, size_t len)
{
	void* ret = dest;
	assert(dest);
	assert(src);

	while (len--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	return ret;
}

int main()
{
	const char* src = "abcd";
	char dest[30] = { "12345" };
	//test1.普通测试
	printf("before:%sn", dest);
	printf("after:%sn", (char*)myMemcpy(dest, src, strlen(src)));
	//test2.正向自我拷贝:
	myMemcpy(dest, dest + 1, (strlen(dest) - 1));
	printf("正向自我拷贝:%sn", dest);
	//test3.逆向自我拷贝
	myMemcpy(dest + 1, dest, (strlen(dest) - 1));
	printf("逆向自我拷贝:%sn", dest);

	system("pause");
	return 0;
}

  • 逆向拷贝的错误体现了其不能自我拷贝
  • 具体原因:出现内存重叠,该函数一边拷贝一边将拷贝后的数据当作新数据拷贝给下一个;

2.memmove( 可按字节拷贝两个重叠内存) 原型

模拟实现+特殊应用
#include
#include
#include
#include
#pragma warning(disable:4996)
//模拟实现memmove
//test2与test3为特殊应用
void* myMemmove(void* dest, const void* src, size_t len)
{
	void* ret = dest;
	assert(dest);
	assert(src);
	char* p = (char*)dest;
	char* q = (char*)src;

	if (p > q && p < q + len)  //逆向自我赋值情况,特殊处理
	{
		//从尾部赋值即可 从右向左
		p += (len-1);
		q += (len-1);
		while (len--)
		{
			*p = *q;
			p--;
			q--;
		}
	}
	else
	{
		//从左向右
		while (len--)
		{
			*p = *q;
			p++;
			q++;
		}
	}
	return ret;
}

int main()
{
	const char* src = "abcd";
	char dest[30] = { "12345" };
	//test1.普通测试
	printf("before:%sn", dest);
	printf("after:%sn", (char*)myMemmove(dest, src, strlen(src)));
	//test2.正向自我拷贝:
	myMemmove(dest, dest + 1, (strlen(dest) - 1));
	printf("正向自我拷贝:%sn", dest);
	//test3.逆向自我拷贝
	myMemmove(dest + 1, dest, (strlen(dest) - 1));
	printf("逆向自我拷贝:%sn", dest);

	system("pause");
	return 0;
}

  • 逆向拷贝未出错,与memcpy函数差异既体现于此;
  • 对于库函数来说,方法更复杂也更完善,现如今两函数无差别;

3.memset(按字节内存赋值) 原型

  • 功能:将ptr所指向的地址逐字节将num个字节初始化为value;
  • size_t为无符号整数类型简写;
应用
#include
#include
#include
#pragma warning(disable:4996)

//memset可以使数组的初始化不用循环
int main()
{
	int a[5];
	memset(a, 0, sizeof(a)); //注意不是sizeof(a) / sizeof(a[0])
	int b[5];
	memset(b, 1, sizeof(b)); //按字节初始化 故赋值1不会达到预期
	for (int i = 0; i < 5; i++)
	{
		printf("%d ", a[i]);
	}
	printf("n");
	for (int j = 0; j < 5; j++)
	{
		printf("%d ", b[j]);
	}
	printf("n");

	system("pause");
	return 0;
}


4.memcmp(比较内存内容大小) 原型

  • 比较时严格按照ASCII码处理,包括数字类型;
  • 可以比较ASCII任何字符,而strcmp只能比较字符;
  • 若ptr1>ptr2,返回1;若ptr1=ptr2,返回0;若ptr1
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/395816.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号