前言:
字符串是一种非常重要的数据类型,但是C语言不存在显式的字符串类型,C语言中的字符串都以字符串常量的形式出现或存储在字符数组中。字符串常量适用于那些对它不做修改的字符串函数。同时,C 语言提供了一系列库函数来对操作字符串,这些库函数都包含在头文件 string.h 中。
目录
一、求字符串长度
strlen
strlen函数的模拟实现
二、长度不受限制的字符串函数
strcpy
strcpy函数的模拟实现
strcat
strcat函数的模拟实现
strcmp
strcmp函数的模拟实现
三、长度受限制的字符串函数
strncpy
strncpy函数的模拟实现
strncat
strncat函数的模拟实现
strncmp
strncmp函数的模拟实现
四、字符串查找
strstr
strstr函数的模拟实现
strtok
strtok函数的模拟实现
五、错误信息报告
strerror
六、字符操作
字符分类函数
字符转换
七、内存操作函数
memcpy
memcpy函数的模拟实现
memmove
memmove函数的模拟实现
memset
memcmp
size_t strlen ( const char * str );
源字符串必须以 '\0' 结束。 会将源字符串中的 '\0' 拷贝到目标空间。 目标空间必须足够大,以确保能存放源字符串。 目标空间必须可变。 学会模拟实现。
例1:
#include
#include
int main()
{
int len1 = strlen("abcdef");
printf("%d\n", len1); //6
char arr[] = {'a','b','c','d','e','f'};
//错误写法
int len2 = strlen(arr);
printf("%d\n", len2); //随机值
return 0;
}
执行结果:
例2:
int main()
{
if(strlen("abc") - strlen("abcdef") > 0)
printf("hehe\n");
else
printf("haha\n");
system("pause");
return 0;
}
执行结果:
由于此时的strlen没有进行定义,它的默认类型为无符号类型,那么两个无符号数相加减最后得出的数是一个很大的数,所以最后的结果必然是大于0的
(1)计数器
#include
#include
int my_strlen(const char *str)
{
int count = 0;
assert(str != NULL);
while(*str != '\0')
{
count++;
str++;
}
return count;
}
int main()
{
int len = my_strlen("abcdef");
printf("%d\n", len);
return 0;
}
(2)指针-指针
#include
int my_strlen(const char *str)
{
const char *p = str;
while(*p != '\0')
{
p++;
}
return p-str;
}
int main()
{
int len = 0;
char arr[10]="abcdef";
len = my_strlen(arr);
printf("%d\n", len);
return 0;
}
(3)递归
不创建临时变量求字符串长度
#include
int my_strlen(const char *str)
{
if(*str=='\0')
return 0;
else
return 1+my_strlen(str+1);
}
int main()
{
int len = 0;
char arr[10]="abcdef";
len = my_strlen(arr);
printf("%d\n", len);
return 0;
}
char* strcpy(char * destination, const char * source);
例:
#include
int main()
{
char arr1[] = "abcdefghi";
char arr2[] = "bit";
//错误示范
//char *arr1 = "abcdefghi";
//p指向常量字符串,而常量字符串无法被修改
//char arr2[] = {'b','i','t'};
//由于此时没有给'\0',由于找不到'\0'会导致向后越界访问
strcpy(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
#include
#include
char *my_strcpy(char *dest, const char *src)
{
assert(dest != NULL);
assert(src != NULL);
char *ret = dest;
//拷贝src指向的字符串到dest指向的空间,包含'\0'
while(*dest++ = *src++)
{
;
}
//返回目的的空间的初始地址
return ret;//'\0'
}
int main()
{
char arr1[] = "abcdefgh";
char arr2[] = "bit";
my_strcpy(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
char * strcat ( char * destination, const char * source );
例:
#include
int main()
{
char arr1[30] = "hello\0xxxxxxx";
char arr2[] = "wolrd";
strcat(arr1, arr2);
printf("%s\n", arr1);
//错误示范
char arr3[] = "hello";
char arr4[] = "world";
strcat(arr3, arr4);
printf("%s\n", arr3);
//由于arr3数组没有定义空间大小
//此时就开辟了'hello\0'6个字节
//当arr4数组追加过去后就产生了越界访问,产生报错
return 0;
}
调试结果:
执行结果:
总结:此时我们可以看到,arr2数组内的的字符串从arr1数组中的'\0'开始进行追加
当arr2数组内的字符串追加过去后,后面的‘\0’也一并追加了过去
当目标空间不够大时,就会造成访问越界
#include
#include
char *my_strcat(char *dest, const char *src)
{
assert(dest != NULL);
assert(src);
char *ret = dest;
//1.找到目的字符串的'\0'
while(*dest != '\0')
{
dest++;
}
//2.追加
while(*dest++ = *src++)
{
;
}
return ret;
}
int main()
{
char arr1[30] = "hello";
char arr2[] = "wolrd";
my_strcat(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
int strcmp (const char * str1, const char * str2 );
此函数开始比较每个字符串的第一个字符。 如果它们彼此相等,则继续使用以下对,直到字符不同或到达终止空字符为止。
标准规定:
#include
#include
int main()
{
char *p1 = "qbc";
char *p2 = "abc";
// int ret = strcmp(p1, p2);
// printf("%d\n", ret);
if(strcmp(p1, p2) > 0)
{
printf("p1 > p2\n");
}
else if(strcmp(p1, p2) == 0)
{
printf("p1 == p2\n");
}
else if(strcmp(p1, p2) < 0)
{
printf("p1 < p2\n");
}
return 0;
}
#include
#include
int my_strcmp(const char *str1, const char *str2)
{
assert (str1 && str2);
// 比较
while(*str1++ == *str2++)
{
if(*str1 == '\0')
{
return 0;//等于
}
}
if(*str1 > *str2)
return 1;//大于
else
return -1;//小于
//return (*str1 - *str2);
//通过相减判断大于或小于
}
int main()
{
char *p1 = "abcdef";
char *p2 = "abqwe";
int ret = my_strcmp(p1, p2);
printf("ret = %d\n", ret);
return 0;
}
char * strncpy ( char * destination, const char * source, size_t num );//单位是字节
例1:
int main()
{
char arr1[10] = "abcdefgh";
char arr2[] = "bit";
stcncpy(arr1, arr2, 6);
return 0;
}
调试结果:
例2:
#include
#include
int main()
{
char arr1[10] = "abcdefgh";
char arr2[] = "bit";
strncpy(arr1, arr2, 6);
return 0;
}
调试结果:
由此可见,strncpy函数能拷贝任意长度的字符,当拷贝的字符长度不够拷贝数时,用 '\0' 进行补充,直到拷贝数相等
#include
#include
void my_strncpy(char *dest, const char *src, int n)
{
assert(src);
char* p1 = dest;
const char* p2 = src;
while (n--)
{
*p1++ = *p2++;
}
}
int main()
{
char arr1[20] = "hello"
char arr2[] = "world";
my_strncpy(arr1, arr2, 3);
return 0;
}
char * strncat ( char * destination, const char * source, size_t num );
例1:
#include
#include
int main()
{
char arr1[30] = { "hello\0xxxxxx" };
char arr2[] = "world";
strncat(arr1, arr2, 4);
return 0;
}
调试结果:
例2:
int main()
{
char arr1[30] = { "hello\0xxxxxxxxx" };
char arr2[] = "world";
strncat(arr1, arr2, 8);
return 0;
}
调试结果:
由此可见,不管追加几个数,都会在追加字符串后加上 '\0',而一旦追加数超过了追加字符串的长度,在追加完整字符串后面再加上'\0'后便结束了
#include
#include
#include
void my_strncat(char* dest, const char* src, int len1, int len2, int n)
{
char* ret = dest + len1;
assert(src);
assert(n <= len2);
while ((n--) && (*ret++ = *src++))
{
;
}
}
int main()
{
char arr1[20] = "abcd";
char arr2[] = "efghi";
int len1 = strlen(arr1);
int len2 = strlen(arr2);
int n = 0;
scanf("%d", &n);
my_strncat(arr1, arr2, len1, len2, n);
return 0;
}
int strncmp ( const char * str1, const char * str2, size_t num );
例:
#include
#include
int main()
{
const char* p1 = "abcdef";
const char* p2 = "abcqwer";
int ret = strncmp(p1, p2, 4);
printf("%d\n", ret);
}
执行结果:
#include
#include
#include
int my_strncmp(const char *dest, const char *src, int n)
{
int ret = 0;
assert(dest);
assert(src);
assert(n);
while( (n--) && !(ret = (unsigned char)*dest-(unsigned char)*src) && *dest )
{
dest++;
src++;
}
return ret;
}
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abcedef";
int n = 0;
int ret = 0;
int i = 0;
scanf("%d",&n);
ret = my_strncmp(arr1, arr2, n);
if(ret == 0)
{
for(i=0; i");
for(i=0; i
char * strstr ( const char *, const char * );
例:
int main()
{
char *p1 = "abcdefabcdef";
char *p2 = "def";
char * ret = strstr(p1, p2);
if(ret == NULL)
{
printf("子串不存在");
}
else
{
printf("%s\n", ret);
}
system("Pause");
return 0;
}
执行结果:
由此可得出,当有主字符串中存在两个及以上子串时,优先按第一次出现相同的地址进行打印,并且会一直打印完剩下的字符串
#include
#include
//KMP 算法
char *my_strstr(const char *p1, const char *p2)
{
assert(p1 != NULL);
assert(p2 != NULL);
char *s1 = NULL;
char *s2 = NULL;
char *cur = (char*)p1;//当前指针current
if(*p2 == '\0')
{
return (char*)p1;
}
while(*cur)
{
s1 = cur;
s2 = (char*)p2;
while(*s1 && *s2 && (*s1 == *s2))
{
s1++;
s2++;
}
if(*s2 == '\0')
{
return cur;//找到子串
}
cur++;
}
return NULL;//找不到子串
}
int main()
{
char *p1 = "abcdef";
char *p2 = "def";
char * ret = my_strstr(p1, p2);
if(ret == NULL)
{
printf("子串不存在\n");
}
else
{
printf("%s\n", ret);
}
return 0;
}
char * strtok (char *str, const char *sep);
例:
#include
#include
int main()
{
char arr[] = "[email protected]";
char *p = "@.";
char buf[1024] = {0};
//strtok会改变字符串内容,buf防止原字符串被切割(保护原始数据)
strcpy(buf, arr);
//切割buf中的字符串
char *ret = NULL;
for(ret = strtok(arr, p); ret != NULL; ret = strtok(NULL, p))
{
printf("%s\n", ret);
}
// char *ret = strtok(arr, p);
// printf("%s\n", ret);
// ret = strtok(NULL, p);
// printf("%s\n", ret);
// ret = strtok(NULL, p);
// printf("%s\n", ret);
return 0;
}
执行结果:
#include
char *my_strtok(char *str1 ,char *str2)
{
static char *p_last = NULL;
if(str1 == NULL && (str1 = p_last) == NULL)
{
return NULL;
}
char *s = str1;
char *t = NULL;
while(*s != '\0')
{
t = str2;
while(*t != '\0')
{
if(*s == *t)
{
p_last = s + 1;
if( s - str1 == NULL)
{
str1 = p_last;
break;
}
*s = '\0';
return str1;
}
t++;
}
s++;
}
return NULL;
}
int main()
{
char arr[] = "[email protected]";
char *ret = NULL;
char *p = "@.";
for(ret = my_strtok(arr, p); ret != NULL; ret = my_strtok(NULL, p))
{
printf("%s\n", ret);
}
return 0;
}
char * strerror(int errum);
返回错误码,所对应的错误信息。
例1:
#include
#include
int main()
{
int i = 0;
//1-10错误码的返回返回信息
for(i = 0; i <= 10; i++)
{
printf("%d %s\n", i, strerror(i));
}
system("pause");
return 0;
}
执行结果:
在实际在使用的时候,错误码并非由我们来控制的,而是接收系统返回的错误信息
例2:
#include
#include string.h>
#include
char *str = strerror(errno);//需引用头文件errno.h
printf("%s\n", str);
//errno 是一个全局的错误码的变量
//当c语言的库函数在执行过程中,发生了错误,就会把对应的错误码,复制到errno中
例3:
#include
#include
int main()
{
//打开文件
FILE *pf = fopen("test.txt", "r");
if(pf == NULL)
{
printf("%s\n", strerror(errno));//知道错误的原因
}
else
{
printf("open file success.\n");
}
return 0;
}
执行结果:
函数 | 如果他的参数符合下列条件就返回真 |
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 | 任何可打印字符,包括图形字符和空白字符 |
注:0位假,非0为真
int tolower ( int c );
//tolower 转小写字母
int toupper ( int c );
//toupper 转大写字母
例1:
#include
#include
int main()
{
char ch = tolower('Q');
putchar(ch);
system("pause");
return 0;
}
执行结果:
例2:
#include
#include
int main()
{
//大写字母转小写
char arr[] = "No Mercy";
int i = 0;
while(arr[i])
{
if(isupper(arr[i]))
{
arr[i] = tolower(arr[i]);
}
i++;
}
printf("%s\n", arr);
return 0;
}
执行结果:
在之前的学习中,我们了解了字符串拷贝可以使用strcpy函数,但是strcpy函数具有局限性。
当拷贝的数据不是字符串时,比如说int类型、float类型,还能使用strcpy函数吗?
strcpy函数在拷贝的时候是以\0为字符串拷贝的结束标志,那么在拷贝其它类型数据的时候,拷贝该结束的时候不一定存在\0。所以使用strcpy函数肯定是行不通的。那怎么办呢?
此时我们就可以使用memcpy函数-- - 内存拷贝函数,用来拷贝任意类型数据。
void *memcpy ( void * destination, const void * source, size_t num );
//void* - 通用类型的指针-无类型指针
//dest destination 表示内存拷贝的目的位置
//src source 表示内存拷贝的起始位置
//size_t num 表示内存拷贝的字节数
例:
int main()
{
int arr1[] = { 1,2,3,4,5 };
int arr2[5] = { 0 };
memcpy(arr2, arr1, sizeof(arr1));
return 0;
}
调试结果:
void *my_memcpy ( void *dest, const void *src, size_t num)
{
void *ret = dest;
assert(dest && src);
while (num--)
{
//*(char*)dest = *(char*)src;
//dest = (char*)dest + 1;//++(char*)dest
//src = (char*)src + 1;//++(char*)src
*((char*)dest)++ = *((char*)src)++;
}
return ret;
}
int main()
{
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 0 };
my_memcpy(arr2, arr1, 20);
return 0;
}
void * memmove ( void * destination, const void * source, size_t num );
//void* - 通用类型的指针-无类型指针
//dest destination 表表示内存移动的目的位置
//src source 表示内存移动的起始位置
//size_t num 表示移动内存的字节数
void *my_memmove( void *dest, const void *src, size_t num)
{
void * ret = dest;
assert(dest && src);
if (dest <= src || (char *)dest >= ((char *)src + num))
{
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[10] = {0,1,2,3,4,5,6,7,8,9};
int arr2[10] = {0};
my_memmove(arr1 + 2, arr1, 20);
return 0;
}
void* memset(void* dest, int c, size_t count);
作用:Sets buffers to a specified character.(将缓冲区设置为指定的字符)
以字节为内存设置单位
例:
#include
#include
int main()
{
char arr[] = "abcdefg";
memset(arr, '*', 4);
printf("%s", arr);
return 0;
}
执行结果:
int memcmp ( const void * ptr1,
const void * ptr2,
size_t num );
例:
#include
#include
int main()
{
int arr1[5] = { 1,2,3,4,5 };
int arr2[5] = { 1,2,3,4,8 };
int ret = memcmp(arr1, arr2, sizeof(arr1));
if (ret > 0)
{
printf("arr1 > arr2");
}
else if (ret == 0)
{
printf("arr1 == arr2");
}
else
{
printf("arr1 < arr2");
}
return 0;
}
执行结果: