本文主要介绍一些常见的字符和字符串函数及其模拟实现
所需要的头文件 #include<string.h>
目录
1、求字符串长度 strlen
size_t strlen ( const char * str ); 功能介绍:求字符串长度(string length) 注意事项:字符串以'\0'作为结束的标志,strlen函数返回的是字符串中'\0'前出现的字符个数(不包含'\0'),且传参的字符串必须以'\0'结束。 注意函数的返回值为size_t (无符号的)。
1.1模拟实现 strlen(三种方法)
//1.计数器方法
size_t my_strlen(const char* str)
{
assert(str); //不能为空指针
int count = 0;
while (*str != '\0')
{
sum++;
str++;
}
return count;
}
//2.指针-指针
size_t my_strlen(char* str)
{
assert(str);
char* p = str;
while (*p != '\0')
{
p++;
}
return p-str;
}
//3.递归的方式
size_t my_strlen(char* str)
{
assert(str);
if (*str != '\0')
{
return 1 + my_strlen(str+1);
}
else
{
return 0;
}
}
//主函数
int main()
{
char arr[] = "abcdef";
size_t ret = my_strlen(arr); //unsigned int = size_t
printf("%u\n", ret);
return 0;
}
2、长度不受限制的字符串函数
2.1 字符串复制 strcpy
char * strcpy ( char * destination, const char * source ); 功能介绍:字符串复制(string copy)把含有'\0'结束符的字符串复制到另一个地址空间。 注意事项:src和dest所指内存区域不可以重叠,且dest必须有足够的空间来容纳src的字符串。 返回指向dest的指针。 源字符串必须以 '\0' 结束,会将源字符串中的 '\0' 拷贝到目标空间。 目标空间必须足够大且可变,以确保能存放源字符串。
2.1.1 模拟实现 strcpy
void my_strcpy(char* dest, char* src)
{
assert(dest && src); //不能为空指针
char* ret = dest;
while (*dest++ = *src++)
;
}
int main()
{
char arr1[] = "abcdef";
char arr2[20] = { 0 };
my_strcpy(arr2, arr1);
printf("%s\n", arr2);
return 0;
}
2.2 字符串连接 strcat
char * strcat ( char * destination, const char * source ); 功能介绍:字符串连接(string catenate)把两个char类型连接。 注意事项:把src所指向的字符串(包括“\0”)复制到dest所指向的字符串后面(删除*dest原来末尾的“\0”)。 要保证*dest足够长,以容纳被复制进来的*src。*src中原有的字符不变。返回指向dest的指针。 src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。 源字符串必须以 '\0' 结束。目标空间必须有足够大且可变,能容纳下源字符串的内容。
2.2.1 模拟实现 strcat
void my_strcat(char* dest, const char* src)
{
assert(dest && src);
char* ret = dest;
//1.找到目标空间的末尾\0
while (*dest != '\0')
{
dest++;
}
//2.拷贝字符串
while (*dest++ = *src++)
;
}
int main()
{
char arr1[20] = "hello ";
my_strcat(arr1, "world");
printf("%s\n", arr1);
return 0;
}
2.3 字符串比较 strcmp
int strcmp ( const char * str1, const char * str2 ); 功能介绍:字符串比较(string compare) 当s1<s2时,返回为负数;当s1=s2时,返回值= 0;当s1>s2时,返回正数。 即:两个字符串自左向右逐个字符相比(按ASCII值大小相比较),直到出现不同的字符或遇'\0'为止。如:1."A"<"B" 2."A"<"AB" 3."compare"<"computer" 注意事项:该函数只能比较字符串,即可用于比较两个字符串常量,或比较数组和字符串常量,不能比较数字等其他形式的参数。
2.3.1 模拟实现 strcmp
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
return -1;
//简便写法:return (*str1 > *str2);
}
int main()
{
char arr1[20] = "zhangsan";
char arr2[20] = "zhangsanfeng";
int ret = my_strcmp(arr1, arr2);
if (ret < 0)
printf("<\n");
else if (ret == 0)
printf("==\n");
else
printf(">\n");
return 0;
}
3、长度受限制的字符串函数(推荐使用)
3.1 指定长度的字符串复制 strncpy
char * strncpy ( char * destination, const char * source, size_t num ); 功能介绍:把src所指向的字符串中以src地址开始的前n个字节复制到dest所指的数组中,并返回被复制后的dest。 注意事项: 1)source串长度<=dest串长度(这里的串长度包含串尾NULL字符) 2)source串长度>dest串长度 综上,一般情况下,使用strncpy时,建议将n置为dest串长度(除非你将多个source串都复制到dest数组,并且从dest尾部反向操作),复制完毕后,为保险起见,将dest串最后一字符置NULL,避免发生在第2)种情况下的输出乱码问题。当然,无论是strcpy还是strncpy,保证dest串容量(能容纳下source串)才是最重要的。
3.1.1 模拟实现 strncpy
#include<stdio.h>
#include<assert.h>
void my_strncpy(char* dest, char* src, size_t n)
{
assert(dest && src); //不能为空指针
int i = 0;
char* ret = dest;
while (*dest)
{
*dest++ = *src++;
i++;
if (i == n)
break;
};
}
int main()
{
char arr1[] = "abcdef";
char arr2[] = "hello world";
my_strncpy(arr2, arr1, 5);
printf("%s\n", arr2);
return 0;
}
运行截图:
3.2 指定长度的字符串连接 strncat
char * strncat ( char * destination, const char * source, size_t num ); 功能介绍:把 src 所指向的字符串追加到 dest 所指向的字符串的结尾,直到 n 字符长度为止。 注意事项:src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串。strncat将会从字符串src的开头拷贝n个字符到dest字符串尾部,dest要有足够的空间来容纳要拷贝的字符串。如果n大于字符串src的长度,那么仅将src指向的字符串内容追加到dest的尾部。 strncat会将dest字符串最后的'\0'覆盖掉,字符追加完成后,再追加'\0'。
3.2.1 模拟实现 strncat
void my_strncat(char* dest, const char* src, size_t n)
{
assert(dest && src);
char* ret = dest;
int i = 0;
//1.找到目标空间的末尾\0
while (*dest != '\0')
{
dest++;
}
//2.拷贝字符串
while (*dest++ = *src++)
{
i++;
if (i == n)
break;
}
}
int main()
{
char arr1[20] = "hello ";
my_strncat(arr1, "world", 3);
printf("%s\n", arr1);
return 0;
}
运行截图:
3.3 指定长度的字符串比较 strncmp
int strncmp ( const char * str1, const char * str2, size_t num ); 功能介绍:把 str1 和 str2 进行比较,最多比较前 n 个字节。 当s1<s2时,返回为负数;当s1=s2时,返回值= 0;当s1>s2时,返回正数。
4、字符串查找函数
4.1 字符串查找 strstr
const char * strstr ( const char * str1, const char * str2 ); char * strstr ( char * str1, const char * str2 ); 功能介绍:用于判断字符串str2是否是str1的子串。 如果是,则该函数返回str1字符串从str2第一次出现的位置开始到 str1结尾的字符串; 否则,返回NULL。
4.1.1 模拟实现 strstr
思路:
//1.暴力求解
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 arr1[] = "hins@qq.com";
//char arr2[] = "qq";
char arr1[] = "abbbcdef";
char arr2[] = "bbc";
char* ret = my_strstr(arr1, arr2);
if (ret == NULL)
{
printf("子串不存在\n");
}
else
{
printf("%s\n", ret);
}
return 0;
}
//2、KMP匹配算法 略
4.2 字符串分割 strtok
char * strtok ( char * str, const char * delimiters ); 功能介绍:分解字符串(string token)为一组字符串。s为要分解的字符串,delim为分隔符字符(如果传入字符串,则传入的字符串中每个字符均为分割符)。首次调用时,s指向要分解的字符串,之后再次调用要把s设成NULL。strtok用来将字符串分割成一个个片段。参数s指向欲分割的字符串,参数delim则为分割字符串中包含的所有字符。当strtok在参数s的字符串中发现参数delim中包含的分割字符时,则会将该字符改为\0 字符。在第一次调用时,strtok必需给予参数s字符串,往后的调用则将参数s设置成NULL。每次调用成功则返回指向被分割出片段的指针。 strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。 注意事项:strtok函数会破坏被分解字符串的完整,调用前和调用后的s已经不一样了.所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。
//使用方法
int main()
{
const char* sep = "@.";
char email[] = "hins@qq.com.net";
//char* ret = my_strstr(arr1, arr2);
char cp[30] = { 0 };
strcpy(cp, email);
char* ret = NULL;
for (ret = strtok(cp, sep);
ret != NULL;
ret = strtok(NULL, sep))
{
printf("%s\n", ret);
}
//下面的版本太啰嗦
//char* ret = strtok(cp, sep);
//printf("%s\n", ret);
//ret = strtok(NULL, sep);
//printf("%s\n", ret);
//ret = strtok(NULL, sep);
//printf("%s\n", ret);
//ret = strtok(NULL, sep);
//printf("%s\n", ret);
return 0;
}
运行截图:
5、错误信息报告 strerror
char * strerror ( int errnum ); 通过标准错误的标号,获得错误的描述字符串 ,将单纯的错误标号转为字符串描述,方便用户查找错误。必须包含的头文件:#include <errno.h> #include <string.h> 返回错误码,所对应的错误信息。
//示例代码
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main ()
{
FILE *fp;
fp = fopen("file.txt","r");
if( fp == NULL )
{
printf("Error: %s\n", strerror(errno));
}
return(0);
}
运行截图:
示例错误码:
6、字符分类函数
需要头文件 #include<ctype.h>
函数 | 如果他的参数符合下列条件就返回真 |
iscntrl | 任何控制字符 |
isspace | 空白字符:空格‘ ’,换页‘\f’,换行'\n',回车‘\r’,制表符'\t'或者垂直制表符'\v' |
isdigit | 十进制数字 0~9 |
isxdigit | 十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F |
islower | 小写字母a~z |
isupper | 大写字母A~Z |
isalpha | 字母a~z或A~Z |
isalnum | 字母或者数字,a~z,A~Z,0~9 |
ispunct | 标点符号,任何不属于数字或者字母的图形字符(可打印) |
isgraph | 任何图形字符 |
isprint | 任何可打印字符,包括图形字符和空白字符 |
字符转换函数 | |
tolower | 字符转小写 |
toupper | 字符转大写 |
7、内存操作函数
7.1 内存拷贝 memcpy
void * memcpy ( void * destination, const void * source, size_t num ); 功能介绍:内存拷贝(memory copy)从源内存地址的起始位置开始拷贝若干个字节到目标内存地址中,即从源source中拷贝n个字节到目标destin中。Copies the values of num bytes from the location pointed to by source directly to the memory block pointed to by destination. 注意事项: 1.source和destin所指的内存区域可能重叠,但是如果source和destin所指的内存区域重叠,那么这个函数并不能够确保source所在重叠区域在拷贝之前不被覆盖。而使用memmove可以用来处理重叠区域。函数返回指向destin的指针。 2.如果目标数组destin本身已有数据,执行memcpy()后,将覆盖原有数据(最多覆盖n)。如果要追加数据,则每次执行memcpy后,要将目标数组地址增加到你要追加数据的地址。 3. void*又称泛型指针,什么类型都可以接收。注意:source和destin都不一定是数组,任意的可读写的空间均可。
7.1.1 模拟实现 memcpy
void* my_memcpy(void* dest, const void* src, size_t num)
{
assert(dest && src);
void* ret = dest;
while (num--)
{
*(char*)dest = *(char*)src; //一个字节一个字节的拷贝比较保险
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return ret;
}
int main()
{
int arr1[] = { 0,1,2,3,4,5,6,7 };
int arr2[10] = { 0 };
my_memcpy(arr2, arr1, 28);
return 0;
}
7.2 内存区域复制 memmove
void * memmove ( void * destination, const void * source, size_t num ); 功能介绍:由src所指内存区域复制num个字节到dest所指内存区域memmove用于拷贝字节,如果目标区域和源区域有重叠的话,memmove能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中,但复制后源内容会被更改。但是当目标区域与源区域没有重叠则和memcpy函数功能相同。 注意事项:和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。 如果源空间和目标空间出现重叠,就得使用memmove函数处理。
原理分析:
7.2.1 模拟实现 memmove
void* my_memmove(void* dest, const void* src, size_t num)
{
assert(dest && src);
void* ret = dest;
if (dest < src)
{
//前->后
while (num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
else
{
//后->前
while (num--)
{
*((char*)dest + num) = *((char*)src + num);
}
}
return ret;
}
int main()
{
int arr1[] = { 1,2,3,4,5,6,7 ,8,9,10 };
my_memmove(arr1+2, arr1, 20);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
运行截图:
7.3 按字节比较 memcmp
int memcmp ( const void * ptr1, const void * ptr2, size_t num ); 功能介绍:字节比较函数(memory compare)把存储区 str1 和存储区 str2 的前 n 个字节进行比较。该函数是按字节比较的,位于string.h。
如果返回值 < 0,则表示 str1 小于 str2。
如果返回值 > 0,则表示 str2 小于 str1。
如果返回值 = 0,则表示 str1 等于 str2
7.4 内存设置函数 memset
void * memset ( void * ptr, int value, size_t num ); 功能介绍:内存设置(memory set)是将某一块内存中的内容全部设置为指定的值,这个函数通常为新申请的内存做初始化工作。(以字节为单位初始化)
OK,以上就是本期知识点“常见字符和字符串函数及其模拟实现”的知识啦~~,感谢友友们的阅读。后续还会继续更新,欢迎持续关注哟~