八. strstr函数
1.strstr的定义
char *strstr( const char *str1, const char *str2 );
->1. strstr查找子串(str2)在字符串(str2)中第一次出现的位置,记录并返回该位置的指针,如果找不到,则返回NULL
->2. str1:查找字符串的目标空间 str2:需要查找的对象字符串
->3. 因为strstr函数操作时不会改变形参的指向,所以我们在两个形参前面加上两个const
2.strstr的使用
例1:
记录子串在目标字符串中的位置,然后返回子串首地址
#include <stdio.h>
#include <string.h>
void main(void)
{
char* str1 = "Strive to improve yourself";
char* str2 = "improve";
char* det = strstr(str1, str2);
if(ret == NULL)
{
printf("字符串不存在!");
}
else
{
printf("%s", det);
}
}
运行结果:
例2:
返回子串首地址在目标字符串中的具体位置(地址 - 地址的方法)
#include <stdio.h>
#include <string.h>
void main(void)
{
char* str1 = "Strive to improve yourself";
char* str2 = "improve";
char* det = strstr(str1, str2);
int result = det - str1 + 1;
if (det == NULL)
{
printf("字符串不存在!");
}
else
{
printf("%d", result);
}
}
运行结果:
例3:
记录目标空间中一共出现多少次子串
#include <stdio.h>
#include <string.h>
void main(void)
{
char* str1 = "Strive to improve improve yourself";
char* str2 = "improve";
char* p = str1;
int count = 0;
while (p = strstr(p, str2))
{
count++;
p++;
}
printf("%d", count);
}
运行结果:
3.strstr的模拟实现
#include <stdio.h>
#include <assert.h>
char* My_strstr(const char* str1, const char* str2)
{
assert(str1 && str2);
char* p = str1;
char* s1 = str1;
char* s2 = str2;
while (*p)
{
s1 = p;
s2 = str2;
while (*s1 != '\0' && *s2 != '\0' && *s1 == *s2)
{
s1++;
s2++;
}
if (*s2 == '\0')
return p;
p++;
}
return NULL;
}
int main()
{
char* str1 = "abbbcdf";
char* str2 = "bbc";
const char* det = My_strstr(str1, str2);
if (det == NULL)
{
printf("字符串不存在!");
}
else
{
printf("%s", det);
}
return 0;
}
九. strtok函数
1.strtok的定义
char *strtok( char *strToken, const char *strDelimit );
->1. 作用:切割字符串。将分隔符转换成\0在将前面那个字符串的首地址返回给函数(返回\0前面的字符串)
->2. char *strToken 目标空间
->3. char *strDelimit 标记(分隔符)
2.strtok的使用
例1:
#include <stdio.h>
#include <string.h>
int main()
{
const char* sep = "@.";//三个分隔符 @ . \0 别忘了字符串还带有\0
char email[] = "zhangsan@@nianxi.nbaiwan";
char cp[30] = { 0 };
strcpy(cp, email);
char *ret = strtok(cp, sep);
if(ret != NULL)
{
printf("%s ", ret);
}
return 0;
}
因为strtok函数会改变原字符串中的内容,所以一般都是使用临时拷贝的内容,并且可修改
运行结果:
strtok函数的第一个参数不为NULL,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置
为NULL,函数将在同一个字符串中被保存的位置开始,查找下一个标记
例2:
#include <stdio.h>
#include <string.h>
int main()
{
const char* sep = "@.";
char email[] = "zhangsan@@nianxi.nbaiwan";
char cp[30] = { 0 };
strcpy(cp, email);
char *ret = strtok(cp, sep);
if(ret != NULL)
{
printf("%s\n", ret);
}
ret = strtok(NULL, sep);
if(ret == NULL)
{
printf(第二次没有找到该标记);
}
else
{
printf("%s\n", ret);
}
return 0;
}
如果两个标记在一起中间什么都没有,函数strtok就啥也获取不到,获取不到strtok会直接跳过
不管它,直接去找下一个标记
运行结果:
例3:
第三次分割
#include <stdio.h>
#include <string.h>
int main()
{
const char* sep = "@.";
/* char email[] = "zhangsan@.nianxinbaiwan";*/
char email[] = "zhangsan@@nianxi.nbaiwan";
char cp[30] = { 0 };
strcpy(cp, email);
char* ret = strtok(cp, sep);
if (ret != NULL)
{
printf("%s\n", ret);
}
ret = strtok(NULL, sep);
if (ret == NULL)
{
printf("第二次没有找到该标记");
}
else
{
printf("%s\n", ret);
}
ret = strtok(NULL, sep);
if (ret == NULL)
{
printf("第三次什么也没获取到");
}
else
{
printf("%s\n", ret);
}
return 0;
}
运行结果:
例4:
如果strtok什么都没找到就会返回NULL
第四次从字符串末尾开始向后找,后面已经没有字符串了,所以找不到
#include <stdio.h>
#include <string.h>
int main()
{
const char* sep = "@.";
/* char email[] = "zhangsan@nianxin.baiwan";*/
char email[] = "zhangsan@@nianxi.nbaiwan";
char cp[30] = { 0 };
strcpy(cp, email);
char* ret = strtok(cp, sep);
if (ret != NULL)
{
printf("%s\n", ret);
}
ret = strtok(NULL, sep);
if (ret == NULL)
{
printf("第二次没有找到该标记\n");
}
else
{
printf("%s\n", ret);
}
ret = strtok(NULL, sep);
if (ret == NULL)
{
printf("第三次什么也没获取到\n");
}
else
{
printf("%s\n", ret);
}
ret = strtok(NULL, sep);
if (ret == NULL)
{
printf("第四次什么也没获取到\n");
}
else
{
printf("%s\n", ret);
}
return 0;
}
运行结果:
因为每次都需要用if else来判断,有点重复不好看
所以我们将代码改进一下:
利用for循环
#include <stdio.h>
#include <string.h>
int main()
{
const char *sep = "@.";
char email[] = "zhangsan@nianxin.baiwan";
char pc[30] = { 0 };
strcpy(pc, email);
char *ret =0;
ret = strtok(pc, sep);
for(; ret != NULL; ret = strtok(NULL, sep))
{
printf("%s\n", ret);
}
return 0;
}
3.strtok的模拟实现
例1:
#include <stdio.h>
#include <string.h>
#include <assert.h>
char* My_strtok(char* ps, const char* pc)
{
assert(pc); //不用加上ps因为第二次用的话会传参NULL
static char* str1 = NULL;
static char* str2 = NULL;
static int count = 0;
static int sz1 = 0;
int sz2 = 0;
if(ps != NULL)
{
str1 = ps;
sz1 = strlen(ps);
sz2 = strlen(pc);
for(*ps; *ps != '\0'; ps++)
{
for(int i = 0; i < sz2; i++)
{
if(i == 0)
{
count++;
}
if(*ps == *(pc + i))
{
*ps = '\0';
str2 = ps;
return str1;
}
}
}
else
{
str1 = str2 + 1;
ps = str1;
for(*ps; *ps = '\0'; ps++)
{
for(int i = 0; i < sz2; i++)
{
if(i == 0)
{
count++;
}
if(*ps == *(pc + i))
{
ps = '\0';
str2 = ps
return str1;
}
}
}
if(count == sz1)
{
return NULL;
}
return str1;
}
}
int main()
{
const char* pc ="b";
char email[] ="nianxinbaiwan";
char ps[30] = { 0 };
strcpy(ps, email);
char* ret = My_strtok(ps, pc);
for(; ret != NULL; ret = My_strtok(NULL, pc))
{
printf("%s\n", ret);
}
return 0;
}
十. strerror函数
1.strerror的定义
char *strerror( int errnum );
->1.返回值是字符型的指针
->2.int errnum 错误码:C语言的数据库,在执行失败的时候,都会自动设置错误码
0:No error
1:Operation not permitted
2:No such file or directory
3:No such process
4:Interrupted function call
5:Input/output error
6:No such device or address
7:Arg list too long
8:Exec format error
9:Bad file descriptor
errno - C语言设置的一个全局的错误码存放的变量
2.strerror的使用
#include <stdio.h>
#include <errno.h>
int main()
{
FILE* pf = fopen("idea.txt", "w");
if(pf == NULL)
{
printf("%d", strerror(errno));
return 1;
}
fputc("w");
fclose(pf);
pf = NULL;
return 0;
}
运行结果:
十一. 字符分类函数
包含头文件 ctype
1.isspace -> 判断是否是空白字符,是就返回非0,不是就返回0
int a = isspace(' ');
printf("%d", a);
2.isdigit -> 判断是否是数字字符,是就返回非0,不是就返回0
int a = isdigit('x');
printf("%d", a);
3.iscntrl -> 任何控字符
4.isxdigit -> 十六进制数字,包括十进制数字,小写字母a - f,大写字母A - F
5.islower -> 小写字母a - z
6.isupper 大写字母A - Z
7.isalpha 字母a - z或A - Z
8.isalnum 字母或数字,a - z,A - Z,0 - 9
9.ispunct 标点符号,任何不属于数字或者字母的图形字符(可打印)
10.isgraph 任何图像字符
11.isprint 任何打印字符,包括图形字符和空白字符
十二. tolower函数
1. tolower的使用
printf("%c\n", tolower('W'));
2.tosupper -> 转大写
十三. memcpy
1.memcpy的定义
void* memcpy(void* destination, const void* source, size_t num);
万能copy函数,什么类型的都可以拷贝
memcpy负责拷贝两块独立的空间中的数据
->1. void* destination 目标空间
->2. void* source 源空间
->3. size_t num 源空间总大小
2.memcpy的使用
例1:
整型拷贝
#include <stdio.h>
#include <string.h>
int main()
{
int arr1[] = {1, 2, 3, 4, 5, 6, 7, };
int arr2[10] = { 0 };
memcpy(arr2, arr1, 28);
int i = 0;
for(i = 0; i < sizeof(arr2)/sizeof(arr2[0]); i++)
{
printf("%d ", arr2[i]);
}
return 0;
}
运行结果:
例1:
浮点型拷贝
#include <stdio.h>
#include <string.h>
int main()
{
float arr1[] = { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, };
float arr2[10] = { 0.0 };
memcpy(arr2, arr1, 28);
int i = 0;
for (i = 0; i < sizeof(arr2) / sizeof(arr2[0]); i++)
{
printf("%d ", arr2[i]);
}
return 0;
}
运行结果:
tips:
strcpy和memcpy的区别:
1.复制内容不同,strcpy只能复制字符类型,而memcpy可以复制任意类型
2.所需参数不同,strcpy只需要目标空间和源空间,而memcpy在次之上还需要源空间总大小
3.用途不同,字符类的复制用strcpy,其他类型的复制使用memcpy
3.memcpy的模拟实现
我们写的这个模拟实现的memcpy是不能实现重叠空间的拷贝的
#include <stdio.h>
#include <assert.h>
void* My_memcpy(void* det, const void* src, size_t num)
{
assert(det&&src);
void* p = det;
while(num--)
{
*(char*)det = *(char*)src;
det = (char*)det + 1;
src = (char*)src + 1;
}
return p;
}
为什么不使用(char*)src++,(char*)det++这种写法,因为这种写法有点问题,有些编译器是不支持这样写的,被(char*)强转了就是一个临时的变量,对一个临时的变量进行操作是有问题的
用自定义函数My_memcpy进行重复空间的拷贝
#include <stdio.h>
#include <assert.h>
void* My_memcpy(void* det, const void* src, size_t num)
{
assert(det && src);
void* p = det;
while (num--)
{
*(char*)det = *(char*)src;
det = (char*)det + 1;
src = (char*)src + 1;
}
return p;
}
void test()
{
int a[] = { 1,2,3,4,5,6,7,8,9,10 };
//int b[20] = { 0 };
My_memcpy(a + 2, a, 28);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", a[i]);
}
}
int main()
{
test();
return 0;
}
运行结果:
由上图知:第三第四元素地址被第一第二元素地址覆盖了,后面拿第三第四元素内容和第一第二元素一样,后面的地址跟前面的同理,就会出现如图这样的结果
tips:
上面说了,memcpy用于单独的两个空间(重叠的内存也能实现,但是C中规定它只能用于单独的两个空间),那重叠内存的拷贝应该用什么函数 - > memmove
十四. memmove函数
1.memmove的定义
void *memmove( void *dest, const void *src, size_t count );
负责拷贝重复空间中的数据
->1. void* destination 目标空间
->2. void* source 源空间
->3. size_t num 需要拷贝的字节数
它的目标空间和源空间是同一个空间
2.memmove的使用
#include <stdio.h>
#include <string.h>
int main()
{
int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
memmove(a + 2, a, 20);
int i = 0;
for(i = 0; i < 10; i++)
{
printf("%d ", a[i]);
}
return 0;
}
运行结果:
3.memmove的模拟实现
#include <stdio.h>
#include <assert.h>
void* My_memmove(void* det, const void* src, size_t num)
{
assert(det&&src);
void* p = det;
if (det < src)
{
while (num--)
{
*(char*)det = *(char*)src;
det = (char*)det + 1;
src = (char*)src + 1;
}
}
else
{
while (num--)
{
*((char*)det + num) = *((char*)src + num);
}
}
return p;
}
int main()
{
int a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
My_memmove(a + 1, a + 2, 20);
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", a[i]);
}
return 0;
}
运行结果: