C语言----字符函数和字符串函数

在编程的过程中,我们要经常处理字符和字符串,为了方便操作字符和字符串,c语言标准库中提供的一系列库函数,接下来我们就开始学习与认识他们

1.字符分类函数

c语言中有一系列的函数是专门做字符分类的,也就是一个字符是属于什么类型的字符的

这些函数的使用需要包含一个头文件:ctype.h

用islower举例

格式:int islower (int c)

为什么后面的是int c--用整型接受的,因为传过来的是字符或者对应的ASCII值

那么我们就用整型接受

返回值也是int

如果这个字符是小写字母的话,那么返回值就是一个非0数字

如果这个字符是一个大写字符的话,那么返回值就是0

总之:如果括号内的不是小写字母,那么这个函数就会返回一个0

是小写字母就返回一个非0数字

int main()
{
    int ret1 = islower('b');//2
    printf("%d\n", ret1);

    int ret2 = islower('A');//0
    printf("%d\n", ret2);


    int ret3 = islower('0');//字符0不是字母
    printf("%d\n", ret3);
    return 0;
}
int main()
{
    int ret1 = isdigit('A');//不是数字字符就返回0
    printf("%d\n", ret1);

    int ret2 = isxdigit('A');//A是16进制的字符--返回的就是非0值
    printf("%d\n", ret2);

    return 0;
}
//写一个代码,将字符串中的小写字母转换成大写字母,其他字符不变
int main()
{
    char arr[] = "I am a student";//末尾隐藏\0
    //需要遍历字符串--通过下标进行访问
    int i = 0;
    while (arr[i] != '\0')//不等于'\0'就一直遍历数组,直到遇到\0就停止遍历数组
    {
        //判断这个数组内的字符是不是小写字母
        if (islower(arr[i]))
            //如果不是小写字母,返回值就是0,是小写字母返回值就是非0数字,
            //这个函数是用来判断输入的字符是不是小写字母
        {
            //小写字母转大写字母的方法:
            //小写字母的ASCII-32=对应放入大写字母ASCII
            arr[i] = arr[i] - 32;
        }

        i++;//没有遇到'\0'就i++

    }
    printf("%s", arr);
    return 0;
}


//小写字母转大写字母出了这个toupper函数,还可以直接通过小写字母的ASCII-32就可以进行转换了

//那么我们对上面的问题进行改造一下
int main()
{
    char arr[] = "I am a student";//末尾隐藏\0
    //需要遍历字符串--通过下标进行访问
    int i = 0;
    while (arr[i] != '\0')//不等于'\0'就一直遍历数组,直到遇到\0就停止遍历数组
    {
        //判断这个数组内的字符是不是小写字母
        if (islower(arr[i]))
            //如果不是小写字母,返回值就是0,是小写字母返回值就是非0数字,
            //这个函数是用来判断输入的字符是不是小写字母
        {
            //小写字母转大写字母的方法:

            arr[i] = toupper(arr[i]);//小写字母通过toupper这个函数转换为大写字母了
        }

        i++;//没有遇到'\0'就i++

    }
    printf("%s", arr);
    return 0;
}
//对于这个循环还有一种判断是不是小写字母的方法
//if(arr[i]>='a'&&arr[i]<='z')
//在这个区间内的就都是小写字母了

这些字符分类函数主要是进行判断

2.字符转换函数

c语言提供两个字符转换函数

int tolower(int c);//将参数传进去的大写字母转小写

int toupper(int c);//将参数传进去的小写字母转大写

//int main()
//{
//    char ch = toupper('a');
//    printf("%c\n", ch);//打印出来的就是A,大写的A
//
//    //如果传进来的大写字母,那么输出的还是大写字母,不做判断
//    ch = tolower('A');
//    printf("%c\n", ch);//将大写字母转换为小写字母
//
//
//    return 0;
//}
//小写字母转大写字母出了这个toupper函数,还可以直接通过小写字母的ASCII-32就可以进行转换了

//那么我们对上面的问题进行改造一下
int main()
{
    char arr[] = "I am a student";//末尾隐藏\0
    //需要遍历字符串--通过下标进行访问
    int i = 0;
    while (arr[i] != '\0')//不等于'\0'就一直遍历数组,直到遇到\0就停止遍历数组
    {
        //判断这个数组内的字符是不是小写字母
        if (islower(arr[i]))
            //如果不是小写字母,返回值就是0,是小写字母返回值就是非0数字,
            //这个函数是用来判断输入的字符是不是小写字母
        {
            //小写字母转大写字母的方法:

            arr[i] = toupper(arr[i]);//小写字母通过toupper这个函数转换为大写字母了
        }

        i++;//没有遇到'\0'就i++

    }
    printf("%s", arr);
    return 0;
}
//对于这个循环还有一种判断是不是小写字母的方法
//if(arr[i]>='a'&&arr[i]<='z')
//在这个区间内的就都是小写字母了

3.strlen的使用和模拟实现

strlen格式:size_t strlen(const char * str);

统计的是\0之前的个数

strlen的返回值是size_t类型的

//
//int main()
//{
//    char arr1[] = "abcdef";
//    size_t len=strlen(arr1);//输出的数据是6
//    printf("%zd\n", len);
//
//
//    char arr2[] = { 'a','b','c','d','e','f' };//这里面是没有\0的
//    size_t len1 = strlen(arr2);
//    printf("%zd\n", len1);//输出数据是17
//    return 0;
//}
//注意函数的返回值是sizeof_t,是无符号的



int main()
{
    if (strlen("abc") - strlen("abcdef") > 0)
//另一种写法:if(strlen("abc")>strlen("abcdef"))

    {// size_t类型      size_t类型
        printf(">\n");
    }
    else
    {
        printf("<=\n");
    }
    return 0;
}
//输出的结果是>,为什么呢?
//当两个无符号整型进行计算的时候,算出来的结果还是无符号整型的结果
//如果想得到-3的话,那么我们需要将strlen("abc")和strlen("abcdef")强制类型转换为int 类型的数据

strlen的,模拟实现的三种方法

1.计数器的方法

2.指针~指针

size_t my_strlen(const char*str)//返回类型是sizeof_t,因为传过来的是字符串首元素的地址,那么我们用一个字符指针进行接收
{//前面加上const防止被修改
    //利用传过来的首元素的地址,我们遍历数组,统计\0之前的元素个数
    //只要不是\0就统计一个数字
    int count = 0;
    //因为str是指针变量,为了防止str是空指针,我们要进行断言一下
    assert(str != NULL);//如果str为空指针就报错

    while (*str != '\0')
    {
        count++;
        str++;//使指针++,向后走一步
    }
    return count;



}
int main()
{
    char arr[] = "abcdef";
    size_t len = my_strlen(arr);//自己创建一个函数来实现strlen函数
    //传过去一个数组名,字符串首元素的地址
    printf("%zd\n", len);
    return 0;
}
//第二种方法:指针-指针
//两个指针相减就能得到两个指针之间的元素个数了
size_t my_strlen(const char* str)
{
    char* start = str;
    assert(str != NULL);
    while (*str != '\0')
    {
        str++;//走到'\0'前面就停止了,那么到最后str的值是最后一个元素的地址
    }

    return str - start;//因为一开始将第一个元素的地址赋值给start了,那么现在指针相减,得到的就是两个指针之间的元素的个数了

}



int main()
{
    char arr[] = "abcdef";
    size_t len = my_strlen(arr);//自己创建一个函数来实现strlen函数
    //传过去一个数组名,字符串首元素的地址
    printf("%zd\n", len);
    return 0;
}
//这种写法可以不创建临时变量

//使用递归来实现
size_t my_strlen(const char* str)
{
    if (*str != '\0')
    { 
        return 1 + my_strlen(str + 1);
    }
    else
    {
        return 0;
    }
}

int main()
{
    char arr[] = "abcdef";
    size_t len = my_strlen(arr);//自己创建一个函数来实现strlen函数
    //传过去一个数组名,字符串首元素的地址
    printf("%zd\n", len);
    return 0;
}

4.strcpy的使用和模拟实现

功能:拷贝字符串

strcpy---cpoy string

strcpy在拷贝的过程中会将arr1里面的内容包括\0拷贝到arr2里面去

int main()
{
    char arr1[] = "hello world";
    char arr2[20] = { 0 };

    //现在想把arr1里面的hello world放到arr2里面去
    //我们可以用strcpy来实现
    strcpy(arr2, arr1);
    printf("%s\n", arr2);


    return 0;
}

注意注意:

被拷贝的字符串一定要包含'\0',保证strcpy遇到\0就能停止拷贝

1.源头的字符串中必须包含\0,没有\0,strcpy是不能结束的

2.目标空间必须足够大,以确保能存放原字符串

3.目标空间必须是可以修改的

int main()
{
    char arr1[] = "hello world";
    char* p = "xxxxxxxxxxxxxx";//常量字符串--不能修改

    strcpy(p, arr1);//会报错的
    return 0;
}
//模拟实现拷贝
char*my_strcpy(char*dest,const char*src)//传过来的是首元素的地址,返回值是目标空间的起始地址,所以就是char*
{
    //*dest指向的是arr2第
    // 一个元素,*src指向的是arr1第一个元素
    //保证这两个指针不为空指针
    /*assert(src != NULL);
    assert(dest != NULL);*/
    //另一种写法:
    assert(dest && src);//如果dest为空指针,那么括号内就为0,就是假的,就会报错

    char* ret = dest;//将dest存起来
    while (*src != '\0')
    {
        *dest=*src;
        //进行加加操作,换下一个字符和下一个位置进行交换
        src++;
        dest++;
    }//当这个循环结束的时候,\0还没有被拷贝进去
    //最后的时候,src已经++成为\0了,那么现在再次赋值就能将\0拷贝进去了
    *dest =* src;//这里处理的就是\0

    return ret;//直接返回目标空间的起始地址

}
int main()
{
    char arr1[] = "abcdef";//末尾隐藏\0
    char arr2[20] = { 0 };
    //1.直接打印arr2
    my_strcpy(arr2, arr1);
    printf("%s\n", arr2);


    //2.接收返回值
    char* p = my_strcpy(arr2, arr1);
    printf("%s\n", p);
    //直接将返回值放到打印里面也可以

    printf("%s\n", my_strcpy(arr2, arr1));
    return 0;
}


//这个函数返回的是char*,为的是实现链式访问

//strcpy函数返回的是字符串拷贝成功过后,目标空间的起始地址,
//返回值就是arr2的首元素的地址



//总结:
//将arr1拷贝到arr2后,我们可以通过三种方法直接打印arr2
//一种就是直接打印arr2
//还有一种就是根据这个拷贝函数的返回值进行打印
//返回值是一个地址,在函数的一开始我们就将目标函数赋值给另一个临时指针,那么这个临时指针就指向了arr2
//在拷贝过后,我们直接将这个临时指针返回,所以在函数的开头我们用char*
//在返回了临时指针后,我们在主函数就用再创建一个临时指针变量进行返回值的接受,
//因为这个临时指针变量指向的是arr2的其实元素,那么我们就可以用这个临时指针变量直接打印arr2
//有了字符串起始元素的地址,我们就能打印这个字符串了
//对函数部分进行改进,
//思考:能不能将拷贝\0和前面的字符串放在一起呢?

char*my_strcpy(char*dest,const char*src)
{

    assert(dest && src);

    char* ret = dest;
    while (*dest++ = *src++)//因为这里是后置++,所以先带进去数据进行解引用,再进行++
    {
     //arr1里面的字符通过*dest++ = *src++这个代码一个个拷贝到arr2里面去了
      //在最后,*src是\0拷贝过去了,然后因为while循环里面是\0,所以循环停止了
      // 但是\0拷贝到dest里面了 
        ;

    }//这个循环拷贝过去之后判断表达式的值
    //因为是后置++,所以延后产生

    return ret;

}

int main()
{
    char arr1[] = "abcdef";
    char arr2[20] = { 0 };

    my_strcpy(arr2, arr1);
    printf("%s\n", arr2);



    char* p = my_strcpy(arr2, arr1);
    printf("%s\n", p);


    printf("%s\n", my_strcpy(arr2, arr1));
    return 0;
}

5.strcat的使用和模拟实现

原字符串必须有\0结尾

目标字符串也得有\0,否则没办法知道从哪里开始

目标空间必须足够大,能容纳下字符串的内容

目标空间是可以进行修改的,而不是常量不能进行修改

int main()
{
    char arr1[20] = "hello ";
    char arr2[] = "world";

    //如何将arr2里面的字符串追加在arr1后面呢?
    strcat(arr1, arr2);//字符串追加
    printf("%s\n", arr1);
    return 0;
}

//这个函数是如何实现追加呢?
//先找到目标函数的末尾\0的位置
//再将原字符串拷贝过来

//要求目标字符串末尾有\0,因为后面要将原字符串拷贝过去,所以原字符串也要有\0
//要返回目标空间的起始地址
//目标空间是可修改的,但是源头需要进行限制,不能被修改
char* my_strcat(char* dest,const char* src)//返回类型为char*类型的
//dest指向的是arr1的首元素的地址
//src指向的是arr2首元素的地址
{
    char* ret = dest;//将起始位置存起来
    assert(dest && src);//进行断言,防止空指针
    //1.找目标空间的\0
    while (*dest != '\0')
    {
        dest++;
    }//while循环找到\0就停下来
    //2.拷贝----我们在字符串追加的时候,我们要将目标字符串末尾的\0覆盖掉
    while (*dest++ = *src++)//这个代码是进行字符串间的拷贝的,因为上面已经找到\0了,已经停止循环了,并且dest指向了arr1末尾的\0

    {
        ;//写个分号就行了,循环得有一个循环语句
    }

    return ret;//直接返回arr1的起始地址
}
int main()
{
    char arr1[20] = "hello ";
    char arr2[] = "world";

    //如何将arr2里面的字符串追加在arr1后面呢?
    my_strcat(arr1, arr2);//字符串追加
    printf("%s\n", arr1);
    return 0;
}

//这个函数是如何实现追加呢?
//先找到目标函数的末尾\0的位置
//再将原字符串拷贝过来

//要求目标字符串末尾有\0,因为后面要将原字符串拷贝过去,所以原字符串也要有\0



//总结: 我们应该先找到目标字符串末尾\0的位置,这样我们才好追加,
//切记:追加的时候我们要将目标字符串末尾的\0覆盖掉


//在这个追加函数我们用了两个循环,第一个循环是找到\0,第二个循环是将原字符串拷贝到目标字符串的后面去

我们是不能对一个数组自己进行追加的

6.strcmp的使用和模拟实现

strcmp是用来比较两个字符串的

返回值是int

用返回值来比较这两个字符串大小

比较的是对应位置上的字符,如果对应位置字符相等就比较下一对字符

比较的不是字符串的长度,而是对应位置上字符的大小

如果两个字符串相等,返回值就是0

如果前面的字符串大于后面的字符串,那么就会返回一个大于0的数字

如果是后面的字符串大于前面的字符,前面的字符小,就返回一个小于 0的数字

int main()
{
    char arr1[] = "abcdef";
    char arr2[] = "abq";
    int ret=strcmp(arr1, arr2);
    //printf("%d\n", ret);//输出的结果是-1,就说明arr1<arr2
    if (ret == 1)//仅仅在vs平台可以写返回值为1,其他的平台就不知道返回值是多少了
    {
        printf(">" );
    }
    else
    {
        printf("<=");
    }
    return 0;
}
//
//int main()
//{
//    char arr1[] = "abcdef";
//    char arr2[] = "abq";
//    int ret=strcmp(arr1, arr2);
//    //printf("%d\n", ret);//输出的结果是-1,就说明arr1<arr2
//    if (ret == 1)//仅仅在vs平台可以写返回值为1,其他的平台就不知道返回值是多少了
//    {
//        printf(">" );
//    }
//    else
//    {
//        printf("<=");
//    }
//    return 0;
//}


//int my_strcmp(const char*str1,const char*str2)//返回值是Int,并且加上const进行限制
//{
//    //我们一对一对字符进行比较
//    assert(str1 && str2);
//    while (*str1 == *str2)
//    {
//        if (*str1 == '\0')//如果str1在上一轮循环++到\0之后,就说明这两个字符串已经完全相等了
//        {
//            return 0;//两个字符串完全相等
//        }
//        str1++;
//        str2++;
//    }//两个字符相等我们就找下一对字符进行比较
//    if (*str1 > *str2)
//    {
//        return 1;
//    }
//    else//*str1 < *str2
//    {
//        return -1;
//    }
//}

//另一种写法
int my_strcmp(const char* str1, const char* str2)//返回值是Int,并且加上const进行限制
{
    //我们一对一对字符进行比较
    assert(str1 && str2);
    while (*str1 == *str2)
    {
        if (*str1 == '\0')//如果str1在上一轮循环++到\0之后,就说明这两个字符串已经完全相等了
        {
            return 0;//两个字符串完全相等
        }
        str1++;
        str2++;
    }//两个字符相等我们就找下一对字符进行比较
    return (*str1 - *str2);//直接返回他们相减的值

}
int main()
{
    char arr1[] = "abcdef";
    char arr2[] = "abcdef";
    int ret=my_strcmp(arr1, arr2);
    printf("%d\n", ret);
    return 0;
}

7.strncpy函数的使用和模拟实现

int main()
{
    char arr1[20] = "abcdef";
    char arr2[20] = { 0 };
    strncpy(arr2, arr1, 3);
    printf("%s", arr2);//打印的结果就是abc,只选择arr1里面的前三位字符进行拷贝
    return 0;
}



int main()
{
    char arr1[20] = "abcdef";
    char arr2[20] = "xxxxxxxx";

    strncpy(arr1, arr2, 3);
    printf("%s", arr1);//输出xxxdef
    return 0;
}

拷贝n个字符从原字符串到目标空间

如果原字符串的长度小于n的话,则拷贝完原字符串之后,在目标的后边追加0,直到凑齐n个

strncpy函数的模拟实现 

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <assert.h>
char* my_strncpy(char* des, const char* src, size_t num)
{
    assert(des && src);//防止空指针
    char* ps = des;//保留最初的位置
    while (num--)
    {
        //防止指针越界
        if (*src == '\0')
        {
            *des++ = '\0';
        }
        else
            *des++ = *src++;//交换
    }
    return ps;
}

int main()
{
    char arr[] = "xxxxxxx";
    char arr1[] = "abcdef";
    my_strncpy(arr, arr1, 7);//仅仅只选择arr1的前7位进行拷贝
    printf("%s\n", arr);
    return 0;
}

8.strncat函数的使用和模拟实现

函数的使用

可以用来给自己追加

在原有的基础上,可以选择性的追加n个字符

函数的模拟实现

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <assert.h>
char* my_strncat(char* des, const char* src, size_t num)
{
    assert(des && src);
    char* ps = des;//记录起始位置
    //我们要找到arr'\0'的位置,再将arr1追加在arr后面
    while (*des)
    {
        des++;
    }
    while (*des++ = *src++)
        ;
    *des = '\0';

    return ps;
}
int main()
{
    char arr[20] = "hello ";
    char arr1[] = "world";
    my_strncat(arr, arr1, 7);
    printf("%s\n", arr);
    return 0;
}

9.strncmp函数的使用和模拟实现

函数的使用

int main()
{
    char arr1[20] = "abcdef";
    char arr2[20] = "abc";

    int ret=strncmp(arr1, arr2, 3);//比较三个字符
    printf("%d", ret);//现在返回值就是0
    return 0;
}

指定几个字符进行比较久进行几个字符比较

总之来说还是比较方便的

模拟实现

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <string.h>
#include <assert.h>
char* my_strncpy(char* des, const char* src, size_t num)
{
    assert(des && src);//防止空指针
    char* ps = des;//保留最初的位置
    while (num--)
    {
        //防止指针越界
        if (*src == '\0')
        {
            *des++ = '\0';
        }
        else
            *des++ = *src++;//交换
    }
    return ps;
}

int main()
{
    char arr[] = "xxxxxxx";
    char arr1[] = "abcdef";
    my_strncpy(arr, arr1, 7);//仅仅只选择arr1的前7位进行拷贝
    printf("%s\n", arr);
    return 0;
}

10.strstr的使用和模拟实现

在一个字符串里面找子字符串,在一个字符串里面找另外一个字符串是否出现

如果要找的字符串出现一次以上,那么我们就返回第一次出现的位置

在str1中找str2

如果在str1中没有找到str2的话,就会返回一个空指针NULL

int main()
{
    char arr[] = "abcdefabcdef";
    char* p = "efab";//定义了一个指向字符串常量"efab"的指针p
    char* ret=strstr(arr, p);//返回的是e的地址
    //printf("%s", ret);//打印结果是efabcdef


    if (ret == NULL)
    {
        printf("不存在\n");
    }
    else
    {
        printf("%s\n", ret);
    }
    return 0;
}
char* my_strstr(const char* str1, const char* str2)//我们只是希望在str1中查找str2,并不希望将这两个字符串修改了,所以要加上const
{
    assert(str1 && str2);//保证两个指针不是空指针
    const char* s1 = NULL;//加上const限制住
    const char* s2 = NULL;
    const char* cur = str1;//一开始指向的是str1的起始位置的
    //*cur != '\0'简化如下:当*cur是\0我们就进不去循环了

    if (*str2 == '\0')//特殊情况,假如str2是空指针,那么我们直接返回str1
    {
        return str1;
    }
    while (*cur)//如果等于'\0'的话就说明这个字符串已经找完了
    {//只要*cur不是\0就能一直寻找要找的字符串
        //分别将起始位置赋值给s1和s2
        s1 = cur;
        s2 = str2; 
        //*s1!='\0'&& *s2!= '\0'简化如下,效果还是一样的
        //就是反正你*s1和*s2是\0这个循环就进不去,直接跳出来了
        while(*s1&& *s2&& * s1 == *s2)//判断两个指针指向位置的字符是否相等
        {
            //如果这一对字符相等的情况下我们就往后走判断下一对字符
            s1++;
            s2++;
        }
        if (*s2 == '\0')//说明我们已经在str1里面已经找完了字符串
        {
            return cur;//那么我们就直接返回str1中我们记录的cur的位置
        }

        cur++;//如果*s1!=*s2的话,就cur++换下一个字符,就跳出这个while循环了
        //再次循环就s1又被重新赋值了,但是s2仍然是被srt2赋值,

        //就是相等与我们在仅仅只是将str1的出发点进行了更换,但是str2的还没变
        //直到能在str1里面找到str2了,就是str2语言\0了,就说明已经在str1里面找到str2了

    //如果*s1不为\0,但是*s2已经是\0了,那么这个while循环我们就跳出来了

    }
    return NULL;//如果cur为\0就是我们已经不可能在str1中找到str2了,那我们直接返回空指针


}
int main()
{
    char arr[] = "abcdefabcdef";
    char* p = "bbs";
    const char* ret=my_strstr(arr, p);



    if (ret == NULL)//根据返回值进行判断str1中是否存在str2
    {
        printf("不存在\n");
    }
    else
    {
        printf("%s\n", ret);
    }
    return 0;
}
//如果在str1里面提前遇到了\0就说明这个字符串已经找完了还没遇到要找的字符串
//但是str2提前遇到\0的话,就说明我们已经找到了要找的字符串了




//总结:
/*
我们在这个模拟函数中,我们最重要的就是创建了一个cur来不断重新定义来找的位置

假如在第一次寻找的过程中,我们没找到,那么cur就进行++操作,然后s1=str2重新赋值,
就是我们将要找的字符串的指针重新定义在首元素,但是cur一直在往后走,
直到s2走到\0,就是说明我们已经在str1内找到str2了

那么如果找到了的话,我们就将cur现在的地址return 回去,就是说明我们在str1中cur处可以找到str2了


*/

我们当前写的strstr函数的实现不是最优的算法

KMP算法---字符串中找字符串---效率更高,但是实现更难

11.strtok函数的使用

charstrtok(charstr,const char *sep)

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

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

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

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

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

6.如果字符串中不存在更多的标记,则返回NULL指针,就是说明这个字符串已经被找完了,再没有任何的分隔符了,已经尽数转化为\0了

//int main()
//{
//    char arr[] = "abdjskgb@vsfkv.net";
//    char buf[256] = { 0 };
//    strcpy(buf, arr);//把arr数据拷贝到buf内//abdjskgb\0vsfkv\0net
//    char sep[] = "@.";//char*sep= "@."
//    char* ret=strtok(buf, sep);
//    printf("%s\n", ret);//输出结果就是abdjskgb
//
//    char* ret1 = strtok(NULL, sep);
//    printf("%s\n", ret1);//输出结果就是vsfkv
//
//
//    char* ret2 = strtok(NULL, sep);
//    printf("%s\n", ret2);//输出结果是net
//
//
//    return 0;
//}
//strtok的返回值是buf的第一个标记的指针
//当这个函数返回的是一个空指针的时候,就说明这个函数已经找完了

//上面这种写法必然是错误的,我们必须先知道提供的字符串需要切割几段



int main()
{
    char arr[] = "abdjskgb@vsfkv.net";
    char buf[256] = { 0 };
    strcpy(buf, arr);//把arr数据拷贝到buf内//abdjskgb\0vsfkv\0net
    char *sep = "@.";//分割符
    char* ret = NULL;
    //上个代码的buf只在strtok里面进行一次,后面都是空指针
    //我们利用了for循环的特点,初始化只执行一次

    //就是说只有第一次传的是buf,后面传的都是NULL
    for (ret = strtok(buf, sep); ret != NULL;ret=strtok(NULL,sep))
    {
        printf("%s\n", ret);
    }
    //只要ret不等于NULL这个循环就一直执行
    //strtok(buf, sep)的返回值是第一个切割符前面的字符串的地址,并将其赋值给ret,

    //每次循环都会运行ret=strtok(NULL,sep),将新获得的返回值赋值给ret,然后每次循环就从新位置开始
    return 0;
}


//总结:我们定义一个数组arr,里面带有分隔符
// 再定义一个空数组,将带有分隔符的数组拷贝过来,在后面的过程,我们都是用这个拷贝的数组
// 
// char* ret = NULL;:定义了一个指向分割后子字符串的指针。
//char *sep = "@."; 定义了分隔符字符串,包含 @ 和 .。



/*针对这个循环进行更加详细的解释

ret = strtok(buf, sep)是初始化部分,
在循环开始之前,strtok被调用,使用buf和sep来分割字符串,并返回第一个子字符串的指针
这个指针被赋值给ret,作为循环的起始点

循环条件:ret != NULL   这表示只要strtok返回的指针不是NULL,就能继续执行循环体,
因为strtok在没有更多子字符串可供分割时会返回NULL,所以在没有更多子字符串可供分割时会结束

迭代部分:ret = strtok(NULL, sep)   
在每次循环迭代时,strtok(NULL, sep)被调用,告知strtok继续从上一次的位置继续分割字符串
并返回下一个子字符串的指针,这个指针被赋值给ret,作为下一次循环的起始点


这个循环的条件保证了每次循环迭代都能够正确地从输入字符串中分割出一个子字符串,
并且在没有更多子字符串可供分割时结束循环。

*/


//strtok可以把一个字符串切成一段一段的,每切一次就将起始地址返回去

//每次就直接将分隔符前面的字符串切割下来,并将切割符变为\0,就是\0后面的字符将不进行访问


//函数会找到第一个分隔符,并记住位置,下次找就从这个位置开始找

12.strerror函数的使用

strerror可以将错误对应的错误信息字符的地址返回

strerror 函数可以把参数部分错误码对应的错误信息的字符串地址返回来。

在不同的系统和C语⾔标准库的实现中都规定了⼀些错误码,⼀般是放在 errno.h 这个头⽂件中说明的,C语⾔程序启动的时候就会使⽤⼀个全局的变量errno来记录程序的当前错误码,只不过程序启动的时候errno是0,表⽰没有错误,当我们在使⽤标准库中的函数的时候发⽣了某种错误,就会将对应的错误码,存放在errno中,⽽⼀个错误码的数字是整数很难理解是什么意思,所以每⼀个错误码都是有对应的错误信息的。strerror函数就可以将错误对应的错误信息字符串的地址返回。

//int main()
//{
//    for (int i = 0; i < 10; i++)
//    {
//        printf("%d:%s\n", i,strerror(i));
//    }
//    return 0;
//}

//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



int main()
{
    //打开文件
    FILE*pf=fopen("date.txt", "r");//r---读,以读文件的形式打开文件,如果文件不存在就是打开失败
    //打开失败就返回一个空指针

    if (pf == NULL)//打开失败
    {
        printf("打开文件失败,原因是%s",strerror(errno));//为了使用errno我们要包含头文件#include <errno.h>
        return 1;//打开失败我们就直接结束进程
    }
    else
    {
        printf("打开成功\n");
        fclose(pf);//关闭文件
        pf = NULL;
    }
    return 0;
}
//打开文件失败,原因是No such file or directory

strerror---将错误码对应的错误信息的字符串的起始地址返回

int main()
{
    //打开文件
    FILE*pf=fopen("date.txt", "r");//r---读,以读文件的形式打开文件,如果文件不存在就是打开失败
    //打开失败就返回一个空指针

    if (pf == NULL)//打开失败
    {
        printf("打开文件失败,原因是:%s\n",strerror(errno));//为了使用errno我们要包含头文件#include <errno.h>
        perror("打开文件失败,原因是");
        return 1;//打开失败我们就直接结束进程

    }
    else
    {
        printf("打开成功

perror--将errno中错误信息直接打印出来

perror函数线打印str指向的字符串,再打印冒号,再打印空格,再打印错误码对应的信息

atoi函数的使用与模拟实现

什么是atoi函数

atoi函数是将字符串转换为整数

基本格式

int atoi (const char * str)

返回值是int

参数:str是要转换为整数的字符串

函数atoi使用细节

1.当参数位置传入的为NULL指针时会报错,当参数位置传入的为空字符串时,返回值为0;

2.atoi函数会自动过滤开始时的空白字符;

3.atoi函数会根据字符串内容自动判断整数的正负;

4.当字符串的数字大小超过了整型数字的取值范围时,返回时会变为整型数据的最大或最小值;

5.当字符串中的数字有非10进制字符隔断时,atoi函数会直接返回当前位置前面的数字;

//int main()
//{
//    int ret = atoi("123");
//    printf("%d ", ret);//输出值就是123
//    return 0;
//}

//1.参数是NULL的话,那么这个程序就会报错的
//int main()
//{
//    char str[] = "241";//创建字符数组
//    int ret = atoi(NULL);//如果参数是NILL的话,那么就会报错的
//    printf("%d", ret);
//
//    return 0;
//}



//2.如果参数是一个空字符串的话,那么就是返回的是0
//int main()
//{
//    char str[] = "";
//    int ret = atoi(str);
//    printf("%d", ret);
//
//    return 0;
//}


//3.atoi函数会自动过滤参数里面的空字符的
//int main()
//{
//    char str[] = "      -54321";
//    int ret = atoi(str);
//    printf("%d", ret);
//
//    return 0;
//}


//4.字符串的数字大小超过了整型数字的取值范围时,返回时会变为整型数据的最大或最小值

//int main()
//{
//    char str[] = "238947283649234";//这个字符串内的数字过于大,那么返回值就是c语言规范中的最大值
//    int ret = atoi(str);
//    printf("%d", ret);
//
//    return 0;
//}


//5.当字符串中的数字有非10进制字符隔断时,atoi函数会直接返回当前位置前面的数字
//int main()
//{
//    char str[] = "-543abc21";//参数中间被abc隔开,那么这个函数就会选取前面的数字
//    int ret = atoi(str);
//    printf("%d", ret);
//
//    return 0;
//}
  • 48
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 18
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值