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

news2024/12/27 0:58:10

在编程的过程中,我们要经常处理字符和字符串,为了方便操作字符和字符串,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个

8.strncat函数的使用

可以用来给自己追加

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

9.strncmp函数的使用

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

    int ret=strncmp(arr1, arr2, 3);//比较三个字符
    printf("%d", ret);//现在返回值就是0
    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指向的字符串,再打印冒号,再打印空格,再打印错误码对应的信息

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1818545.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

yolov8数据处理方式

数据处理 在YOLOv8中&#xff0c;数据处理是非常重要的一环&#xff0c;它包括数据加载、数据增强、数据预处理和数据标签处理等步骤。以下是YOLOv8中常用的数据处理方式及常用处理方法的介绍&#xff1a; 1.数据加载&#xff1a; 数据加载是指将训练和测试数据加载到模型中进…

像素蛋糕Photoshop颜色导出不一致问题分析与解决

问题点&#xff1a;发现用像素蛋糕修完图明天应该为最右边图片显示 模特应该是白皙的&#xff0c;但是导出图片无论是否勾选SRGB都表现的为种间图片颜色一样 饱和度巨高。 问题分析&#xff1a;那这一定是颜色配置文件出现问题&#xff0c;找到客服表示可以去PS打开看是否与预…

沉降观测点的定义、重要性、建设与选择

沉降观测点&#xff0c;简称沉降点&#xff0c;是指在建筑物、构筑物或地基等结构物上设置的用于测量其垂直位移(沉降)的特定位置。这些点通常被标记并安装相应的监测设备&#xff0c;以便长期、连续地监测结构物的沉降情况。 点击输入图片描述&#xff08;最多30字&#xff09…

工业企业的物料主数据管理应该如何做?

前言&#xff1a;如果我们说主数据是企业的“黄金数据”&#xff0c;那么对于制造型企业来说物料主数据就是企业的“钻石”数据。物料主数据贯穿于制造型企业的设计、工艺、采购、生产、库存、物流、销售的各个环节。做好物料主数据管理&#xff0c;是企业保有竞争力的关键&…

Codeforces Global Round 26 E. Shuffle 【换根DP】

E. Shuffle 题意 给定一颗 n n n 个节点的树 T T T&#xff0c;现在要求恰好执行下面的程序操作一次&#xff1a; 从 T T T 中选择一个点&#xff0c;作为 T 2 T_2 T2​ 的新根节点将这个点从 T T T 中删除&#xff0c;现在 T T T 被分成了一个或一个以上的连通块树对每…

Linux so文件无法找到及某条命令找不到的解决办法

前言 在一些定制软件中可能会自带so文件。或者自带一些二进制命令。 这时会如果运行某个程序会发生 **.so 文件无法找到的错误。 以及 * 某条命令无法找到的错误。 比如像是下面这样 解决办法&#xff1a; so文件无法找到 通过往 LD_LIBRARY_PATH 变量中追加路径来告诉程序…

微软新一代文本转语音模型——笑声,情绪,心情,打造真实AI语音

文本转语音一直是音频领域大家研究的对象&#xff0c;而基于人工智能模型打造的文本转语音的音频总是有机器人的味道&#xff0c;缺乏了人类的感情。如何把人类的感情融入到文本转音频领域一直是各大模型厂家研究的重点。 而OpenAI发布的GPT-4o&#xff08;“o”代表“omni”&a…

老杨说运维 | 基于数据驱动的智观能力建设(文末附现场视频)

本期回顾来自擎创科技创始人兼CEO杨辰的现场演讲 青城山脚下的滔滔江水奔涌而过&#xff0c;承载着擎创一往无前的势头&#xff0c;共同去向未来。2024年6月&#xff0c;双态IT成都用户大会擎创科技“数智化可观测赋能双态运维”专场迎来了完满的收尾。 “没有2200年前李冰率众…

有一个主域名跟多个二级子域名时该怎么申请SSL证书?

当您拥有主域名以及多个子域名时&#xff0c;选择合适的SSL证书类型对于确保网站的安全性至关重要。以下是三种SSL证书类型的简要介绍&#xff1a; 单域名SSL证书&#xff1a; 功能&#xff1a;只能绑定单个域名&#xff0c;无论是主域名还是子域名。 适用场景&#xff1a;仅…

强化学习-tutorial

强化学习 当你发现收集有标注的数据困难&#xff0c;正确答案人类也不知道是什么的时候&#xff0c;往往是考虑使用RL的时候。尽管机器不知道答案是什么&#xff0c;但是机器会知道什么好&#xff0c;什么不好&#xff0c;通过与环境互动获得奖励。 过程 演算法解RL问题&…

二分+ST表+递推,Cf 1237D - Balanced Playlist

一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 Problem - 1237D - Codeforces 二、解题报告 1、思路分析 case3提示我们一件事情&#xff1a;如果存在某个位置永远不停止&#xff0c;那么所有位置都满足永远不停止 很容易证明 随着下标右移&#xff0c…

一键铺货、多商户入驻:了解迅狐多语言跨境商城源码的商业优势

迅狐多语言跨境商城源码是一款创新的电商平台解决方案&#xff0c;具有独特的商业优势。其中&#xff0c;一键铺货和多商户入驻是其核心功能之一&#xff0c;为商家提供了便利和灵活性。 一、一键铺货&#xff1a;简化产品上架流程 对于电商卖家来说&#xff0c;上架商品是一…

LSS 和 BEVDepth算法解读

前言 当前BEV的研究大都基于深度学习的方法&#xff0c;从组织BEV特征信息的方式来看&#xff0c;主流方法分属两类&#xff1a;自底向上方法和自顶向下方法。 自底向上方法比较早的代表工作是LSS&#xff0c;后来BEVDet、BEVDepth等也是基于LSS的框架来进行优化。自底向上方…

qt dll编写和调用

dll编写 新建项目 头文件 #ifndef LIB1_H #define LIB1_H#include "lib1_global.h"class LIB1_EXPORT Lib1 { public:Lib1(); };//要导出的函数&#xff0c;使用extern "C"&#xff0c;否则名称改变将找不到函数extern "C" LIB1_EXPORT int ad…

汽车传动系统为汽车动力总成重要组成部分 我国市场参与者数量不断增长

汽车传动系统为汽车动力总成重要组成部分 我国市场参与者数量不断增长 汽车系统主要包括动力系统、制动系统、传动系统、转向系统、行驶系统、燃油供给系统、照明系统以及电器系统。汽车传动系统指能够将发动机产生的动力转化为车辆行驶驱动力的动力传递装置。汽车传动系统为汽…

什么是电表无人抄表?

1.电表无人抄表&#xff1a;智能时代的新式计量方法 随着科技的发展的迅猛发展&#xff0c;传统电表抄表方法正被一种全新的、高效率的方式所替代——电表无人抄表。这类技术的普及&#xff0c;不仅提升了电力行业的经营效率&#xff0c;同时也为用户增添了更贴心的服务。 2.…

Python邮箱发送如何设置?Python发信方法?

Python邮箱发送邮件需要哪些库&#xff1f;怎么使用Python发信&#xff1f; Python的强大之处在于其丰富的库和模块&#xff0c;使得开发者可以轻松地实现各种功能&#xff0c;包括通过电子邮件发送信息。AokSend将介绍如何在Python中设置和发送电子邮件&#xff0c;以及相关的…

多功能电能表抄表

1.多功能电能表抄表&#xff1a;简述 多功能电能表抄表是一种现代化电力计量方法&#xff0c;它不仅能够纪录电力耗费&#xff0c;还能提供多种多样额外功能&#xff0c;如实时检测、故障预警、远程操作等。相对于传统电度表&#xff0c;它大大提高了电力管理的效率和精确性。…

浅谈网络通信(2)

文章目录 一、TCP1.1、TCP提供的api —— ServerSocket 、Socket1.2、使用TCP协议编写回显服务器1.3、长/短连接 二、应用层协议、传输层协议详解2.1、应用层(后端开发必知必会)2.1.1、自定义应用层协议2.1.2、通用的协议格式2.1.2.1、XML2.1.2.2、json2.1.2.3、protobuffer 2.…

kotlin类型检测与类型转换

一、is与!is操作符 1、使用 is 操作符或其否定形式 !is 在运行时检测对象是否符合给定类型。 fun main() {var a "1"if(a is String) {println("a是字符串类型:${a.length}")}// 或val b a is Stringprintln(b) } 二、"不安全的"转换操作符…