当前位置: 首页 > news >正文

字符函数和字符串函数(1)

#define _CRT_SECURE_NO_WARNINGS

#include<stdio.h>

求字符串长度

    strlen

长度不受限制的字符串函数

    strcpy

    strcat

    strcmp

长度受限制的字符串函数介绍

    strncpy

    strncat

    strncmp

字符串查找

    strstr

    strtok

错误信息报告

    strerror

字符操作

内存操作函数

    memcpy

    memmove

    memset

    memcmp

1. 函数介绍

1.1 strlen

    size_t strlen(const char* str);

strlen的使用

方法一:

int my_strlen(const char* str)
{
    assert(str != NULL);
//保证指针str的有效性//assert——断言
    int i = 0;
    while (*str != '\0')
    {
        str++, i++;
    }
    return i;
}
int main()
{
    char str[] = "abcd";
    int len = my_strlen(str);
    printf("%d\n", len);
    return 0;
}

方法二:递归

int my_strlen(const char* str)
{
    assert(str != NULL);
    if (*str != '\0')
        return 1 + my_strlen(str + 1);
    else
        return 0;
}
int main()
{
    char str[] = "abcd";
    int len = my_strlen(str);
    printf("%d\n", len);
    return 0;
}

方法三:指针 - 指针

int my_strlen(const char* str)
{
    assert(str != NULL);
    char* start = str;
    while (*str)
    {
        str++;
    }
    return str - start;
}
int main()
{
    char str[] = "abcd";
    int len = my_strlen(str);
    printf("%d\n", len);
    return 0;
}

size_t

#include<string.h>
int main()
{
    if (strlen("abc") - strlen("abcdef") > 0)
        printf(">\n");
    else
        printf("<=\n");
    return 0;
}
//输出结果:>

    原因:判断语句中,两个表达式的结果是 size_t 类型,为无符号数。
            不管进行何种四则运算得到的结果也只会是 size_t 类型。
            就以上段代码为例,3 - 6 = -3,由于只能得到 无符号类型的数。
            因此,二者之差也只会得到一个数值相当大的无符号整型。


    字符串已经 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包

    含'\0' )。


    参数指向的字符串必须要以 '\0' 结束。
    注意函数的返回值为size_t,是无符号的( 易错 )

注:

#include <stdio.h>
int main()
{
    const char* str1 = "abcdef";
    const char* str2 = "bbb";
    if (strlen(str2) - strlen(str1) > 0)
    {
        printf("str2>str1\n");
    }
    else
    {
        printf("srt1>str2\n");
    }
    return 0;
}

1.2 strcpy

    char* strcpy(char* destination, const char* source);

#include<string.h>
int main()
{
    char arr1[25] = { 0 };
    char arr2[] = "Chen Xianglin PKU";
//普通字符数组
    strcpy(arr1, arr2);
    char arr3[] = "PKU\0Bob";
    strcpy(arr1, arr3);
    char arr4[3] = { 0 };
    strcpy(arr4, arr2);
    char* arr5 = "abcdef";
//常量字符串
    strcpy(*arr5, arr2);
    char* p = "defabc";
    strcpy(p, arr2);
    return 0;
}

my_strcpy的模拟实现

#include<assert.h>
char* my_strcpy(char* dest, const char* src)
//由于这里是将原数据内容拷贝到目标空间里去,因此原数据内容不必修改,需要修改的仅有目标空间,
//因此,函数第二个参数用 const char*。

{
    char* ret = dest;
    assert(dest && src);
//因为在下面代码中,必定会对这两个指针进行解引用操作,
    //因此必须保证这两个指针的有效性,故必须要用 assert 来进行断言,确保二者不是空指针。

    //思路:
    //由于前面的知识,学到了 strcpy 是将原数据内容,包括 '\0' ,全部拷贝给目标空间。

    while (*dest++ = *src++)//优先级:++ > *
                            //当 *str 是 '\0' 时,*dest = *src,即其也是 '\0',此时循环结束。

    {
        ;
    }
    return ret;
//这里返回的是目标空间的起始地址。
}
int main()
{
    char arr1[20] = "";
    char arr2[] = "Hello,PKU.";
    my_strcpy(arr1, arr2);
    printf("%s\n", arr1);
    return 0;
}

    Copies the C string pointed by source into the array pointed by destination, including
    the terminating null character(and stopping at that point).
    源字符串必须以 '\0' 结束。
    会将源字符串中的 '\0' 拷贝到目标空间。
    目标空间必须足够大,以确保能存放源字符串。
    目标空间必须可变。
    学会模拟实现。


1.3 strcat:字符串追加

    char* strcat(char* destination, const char* source);

    Appends a copy of the source string to the destination string.The terminating null
    character in destination is overwritten by the first character of source, and a null -
    character is included at the end of the new string formed by the concatenation of both

    in destination.
    源字符串必须以 '\0' 结束。
    目标空间必须有足够的大,能容纳下源字符串的内容。
    目标空间必须可修改。

    字符串自己给自己追加的话,会怎么样?

    arr[] = "abcdef";
    打印结果:abcdefabcdefabcdef……进入死循环
    原因:找到'\0'后,开始追加。但是当追加完 'f' 后,原来的 '\0' 已经被 a 替换掉了。
    因此就没有结束标志了,即陷入死循环。

int main()
{
    char arr[20] = "Chen Xianglin ";
    strcat(arr, "PKU");
    printf("%s\n", arr);

    return 0;
}

模拟实现 strcat 函数

#include<assert.h>
char* my_strcat(char* strDest, const char* strSrc)
{
    char* ret = strDest;
    assert(strDest && strSrc);
  
 //思路:
    //①找到目标字符串的 '\0';

    while (*strDest != '\0')//while(*strDest)
    {
        strDest++;
    }
    
//错误代码:
    //while(*strDest++)
    //{
    //    ;
    //}
    //错误原因:
    //第一次循环:++,strDest指针指向 'h', 但 *strDest++ 表达式结果是 'C';
    //第二次循环:++,strDest指针指向 'e', 但 *strDest 表达式结果是 'h';
    //第三次循环:++,strDest指针指向 'n', 但 *strDest 表达式结果是 'e';
    //第四次循环:++,strDest指针指向 ' ', 但 *strDest 表达式结果是 'n';
    //第五次循环:++,strDest指针指向 'X', 但 *strDest 表达式结果是 ' ';
    //第六次循环:++,strDest指针指向 'i', 但 *strDest 表达式结果是 'X';
    //第七次循环:++,strDest指针指向 'a', 但 *strDest 表达式结果是 'i';
    //第八次循环:++,strDest指针指向 'n', 但 *strDest 表达式结果是 'a';
    //第九次循环:++,strDest指针指向 'g', 但 *strDest 表达式结果是 'n';
    //第十次循环:++,strDest指针指向 'l', 但 *strDest 表达式结果是 'g';
    //第十一次循环:++,strDest指针指向 'i', 但 *strDest 表达式结果是 'l';
    //第十二次循环:++,strDest指针指向 'n', 但 *strDest 表达式结果是 'i';
    //第十三次循环:++,strDest指针指向 ' ', 但 *strDest 表达式结果是 'n';
    //第十四次循环:++,strDest指针指向 '\0', 但 *strDest 表达式结果是 ' ';
    //第十五次循环:++,strDest指针指向 '\0', 但 *strDest 表达式结果是 '\0',跳出循环,
    //                从而导致追加时,源数据内容是从第二个 '\0' 开始追加的。

    //②追加。
    while (*strDest++ = *strSrc++)
    {
        ;
    }
    return ret;
}
int main()
{
    char arr[20] = "Chen Xianglin ";
    my_strcat(arr, "PKU");
//传字符串 "PKU" ,实际上传的是首字符 'P' 的地址
    printf("%s\n", arr);

    return 0;
}

1.4 strcmp

    比较字符串内容函数

    int strcmp(const char* str1, const char* str2);

    Return Value
    Returns an integral value indicating the relationship between the strings :
    return value   indicates
    <0             the first character that does not match has a lower value in ptr1 than in ptr2
    =0             the contents of both strings are equal
    >0             the first character that does not match has a greater value in ptr1 than in ptr2

    This function starts comparing the first character of each string.If they are equal to each
    other, it continues with the following pairs until the characters differ or until a
    terminating null - character is reached.
    标准规定:
    第一个字符串大于第二个字符串,则返回大于0的数字
    第一个字符串等于第二个字符串,则返回0
    第一个字符串小于第二个字符串,则返回小于0的数字

    那么如何判断两个字符串?

#include<string.h>
int main()
{
    char arr1[] = "abcdef";
    char arr2[] = "abdcefg";
    int ret = strcmp(arr1, arr2);
//返回结果:-1
    printf("%d\n", ret);
    return 0;
}

strcmp 模拟实现

方法一:

int my_strcmp(const char* e1, const char* e2)
{
    assert(e1 && e2);
    if (*e1 == *e2)
    {
        while (*e1 == *e2 && *e1 != '\0')
        {
            e1++, e2++;
            if (*e1 > *e2)
                return 1;
            else if (*e1 < *e2)
                return -1;
        }
        return 0;
    }
    else
    {
        if (*e1 > *e2)
            return 1;
        else if (*e1 < *e2)
            return -1;
    }
}
int main()
{
    char arr1[] = "abcdef";
    char arr2[] = "abdcefg";
    int ret = my_strcmp(arr1, arr2);
    printf("%d\n", ret);
    return 0;
}

方法二:

int my_strcmp(const char* e1, const char* e2)
{
    assert(e1 && e2);
    while (*e1 == *e2)
    {
        if (*e1 == '\0')
            return 0;
        e1++, e2++;
    }
    if (*e1 > *e2)
        return 1;
    else
        return -1;
}
int main()
{
    char arr1[] = "abcdef";
    char arr2[] = "abdcefg";
    int ret = my_strcmp(arr1, arr2);
    printf("%d\n", ret);
    return 0;
}

方法三:

int my_strcmp(const char* e1, const char* e2)
{
    assert(e1 && e2);
    while (*e1 == *e2)
    {
        if (*e1 == '\0')
            return 0;
        e1++, e2++;
    }
    return *e1 - *e2;
}
int main()
{
    char arr1[] = "abcdef";
    char arr2[] = "abdcefg";
    int ret = my_strcmp(arr1, arr2);
    printf("%d\n", ret);
    return 0;
}

总结:

    strcpy, strcat, strcmp 都是长度不受限制的字符串函数,
                             结束标志或者最坏结束标志( strcmp )都是目标空间或源数据内容里的 '\0'。

    正是由于它们的这种特性,在目标空间不够大等异常情况下,往往会不安全。

    也因此,即将要学的长度受限制的字符串函数孕育而生—— strncpy, strncat, strncmy

1.5 strncpy

    char* strncpy(char* destination, const char* source, size_t num);

    Copies the first num characters of source to destination.If the end of the source C string
    (which is signaled by a null - character) is found before num characters have been
    copied, destination is padded with zeros until a total of num characters have been                written to it.
    拷贝num个字符从源字符串到目标空间。
    如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。

例1:

#include<string.h>
int main()
{
    char arr1[20] = { 0 };
    strncpy(arr1, "Chen Xianglin,PKU...", 18);
    printf("%s\n", arr1);
    return 0;
}

思考:

    strncpy 在进行拷贝时,是只将 num 个字符拷贝进去,还是会在将 num 个字符拷贝进去之后,再加一个 '\0' 呢?
                        
下面我们将用例2这段代码来找出思考中的答案

例2:

#include<string.h>
int main()
{
    char arr1[25] = "XYAFU,Chen Xianglin. ";
    strncpy(arr1, " PKU, ……", 6);
    printf("%s\n", arr1);
    return 0;
}

回答:

    由此可见,strncpy 在进行拷贝时,只将 num 个字符拷贝进去,并不会在后面多加 '\0' 。

思考:

    当拷贝数据内容长度比 num 小时,拷贝完源数据内容部分后,剩余部分如何拷贝?

例3:

#include<string.h>
int main()
{
    char arr1[40] = "****************************************";
    strncpy(arr1, "Welcome to PKU,Chen Xianglin.", 35);
    printf("%s\n", arr1);
    return 0;
}

回答:

    由此可见,当拷贝源数据内容长度比 num 小时,拷贝完源数据内容部分后,剩余部分将由 '\0' 补齐。

strncpy的模拟实现:

思路:

    当 sizeof(源数据内容) 等于 num 时,拷贝完源数据内容为止( '\0' 除外)。
    当 sizeof(源数据内容) 小于 num 时,拷贝完源数据内容后,其余位补 '\0'。
    当 sizeof(源数据内容) 大于 num 时,拷贝完 num 个字符的源数据内容后为止。

#include<assert.h>
char* my_strncpy(char* dest, const char* src, size_t num)
{
    char* ret = dest;
    assert(dest && src);

    int i = 0;
    while (i != num)
        
//当 sizeof(源数据内容) 等于 num 时,
        //(此时 i 必= num )拷贝完源数据内容为止( '\0' 除外)。
        //当 sizeof(源数据内容) 大于 num 时,拷贝完 num 个字符的源数据内容后为止。
        //此时 i 必= num ,故也会跳出循环。

    {
        *dest++ = *src;
      
 //当 sizeof(源数据内容) 小于 num 时,拷贝完源数据内容后,其余位补 '\0'。
        if (*src == '\0' && i < num)
        {
            i++;
            continue;
        }
        src++;
        i++;
    }

    return ret;
}
int main()
{
    char arr[40] = "****************************************";
  
 //char arr[4] = "****";//会引起栈损坏.
    //定义:栈损坏(Stack Corruption):栈内存数据被非法修改(如数组越界、野指针操作)。
    //原因:栈内存中变量 `arr` 附近的区域被意外破坏,
    //        通常由栈内存被非法修改(缓冲区溢出)或非法内存访问引起。

    my_strncpy(arr, "Welcome to PKU,Chen Xianglin.", 5);
    printf("%s\n", arr);
    return 0;
}

1.6 strncat

    char* strncat(char* destination, const char* source, size_t num);

#include<string.h>
int main()
{
    char arr[20] = "PKU,";
    strncat(arr, "Chen Xianglin.", 14);
    printf("%s\n", arr);
    return 0;
}

思考:

    strncat 在进行追加时,是只将 num 个字符追加进目标空间去,还是会在将 num 个字符追加进去之后,再加一个 '\0' 呢?
                        

代码验证:

#include<string.h>
int main()
{
    char arr[30] = "PKU,\0,TSU,XYAFU,XJTU,UESTC,BIT";
    strncat(arr, "Chen Xianglin.", 14);
    printf("%s\n", arr);
    return 0;
}


    经过调试可知,strncat 在进行追加时,会在将 num 个字符追加进去之后,再加一个 '\0' 。

    追加后,arr[30] 里的数据内容为:'P','K','U',',','C','h','e','n',' ','X','i','a',
    'n','g','l','i','n','.','\0','U',',','U','E','S','T','C',',','B','I','T'。

思考:

    当 num 分别 < 或 > strlen(源数据内容) 时,strncat 的追加结果应该是怎样的呢?

代码验证:

①num > strlen(源数据内容)

int main()
{
    char arr[14] = "PKU,\0,TSU,BIT.";
    strncat(arr, "CXL.", 8);
    printf("%s\n", arr);
    return 0;
}

    调试结果为:

    arr[14] 里的内容在经过 strncat 函数追加后,数据内容为:'P','K','U',',','C','X','L',
                                                             '.','\0',',','B','I','T','.'。

    故,可知,strncat 函数在向目标空间追加源数据内容,且当 strlen(源数据内容) >= num 时,
    strncat 会在追加源数据内容后,再在下一个指针出加上 '\0',从而停止追加。

②num < strlen(源数据内容)

int main()
{
    char arr[14] = "PKU,\0,TSU,BIT.";
    strncat(arr, "CXL....", 4);
    printf("%s\n", arr);
    return 0;
}

    调试结果为:

    arr[14] 里的内容在经过 strncat 函数追加后,数据内容为:'P','K','U',',','C','X','L',
                                                             '.','\0',',','B','I','T','.'。

    故,可知,strncat 函数在向目标空间追加源数据内容,
    且当 strlen(源数据内容) 不管是否等于 num 时,
    strncat 会在追加完 num 个字符的源数据内容后,最后再在下一个指针出加上 '\0',从而停止追加。

strncat 的模拟实现

char* my_strncat(char* dest, const char* src, size_t num)
{
    char* ret = dest;

    //思路:
    //①找出目的空间的 '\0' 的位置;
    //②找到位置后,开始追加,将源数据内容的 '\0'也追加进去之后为止。

    //①
    while (*dest)
    {
        dest++;
    }
 
   //②
    int i = 0;
    while (*dest++ = *src++)
    {
        i++;
        if (i == num)
        {
            *dest = '\0';
            break;
        }
    }
    return ret;
}
int main()
{
    char arr[15] = "PKU,\0**********";
    my_strncat(arr, "CXL.", 3);
    
//my_strncat(arr, "CXL.", 6);
    //my_strncat(arr, "CXL.", 4);

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

    Appends the first num characters of source to destination, plus a terminating null -              character.
    If the length of the C string in source is less than num, only the content up to the
    terminating null - character is copied.

/* strncat example */
#include <stdio.h>
#include <string.h>

int main()
{
    char str1[20];
    char str2[20];
    strcpy(str1, "To be ");
    strcpy(str2, "or not to be");
    strncat(str1, str2, 6);
    puts(str1);
    return 0;
}

1.7 strncmp

    int strncmp(const char* str1, const char* str2, size_t num);

    比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完。

#include<stdio.h>
#include<string.h>

int main()
{
    char* p1 = "PKU,CXL.";
    char* p2 = "PKU,Chen Xianglin.";//Z(90) < a(97)
    int ret = strncmp(p1, p2, 5);
    printf("%d\n", ret);
    return 0;
}

strncmp 模拟实现

方法①:

#include<string.h>
#include<assert.h>

int my_strncmp(const char* dest, const char* src, size_t num)
{
    assert(dest && src);
    size_t i = 1;
    while (*dest++ == *src++)
    {
        if (*dest == '\0' || i == num)
            return 0;
        i++;
    }
    return *dest - *src;
}
int main()
{
    //char* p1 = "PKU,Chen Xianglin.";
    char* p1 = "PKU,CXL.?";
    char* p2 = "PKU,CXL.!";//Z(90) < a(97)
    int ret = my_strncmp(p1, p2, 6);
    printf("%d\n", ret);
    return 0;
}

方法②:

#include<string.h>
#include<assert.h>

int my_strncmp(const char* dest, const char* src, size_t num)
{
    assert(dest && src);
    size_t i = 1;
    while (*dest++ == *src++)
    {
        if (*dest == '\0' || i == num)
            return 0;
        i++;
    }
    if (*dest > *src)
        return 1;
    else
        return -1;
}
int main()
{

    //char* p1 = "PKU,Chen Xianglin.";
    //char* p1 = "PKU,CXL.";
    //char* p1 = "";
    char* p1 = "Welcome to PKU.";
    //char* p2 = "PKU,Chen Xianglin.";
    //char* p2 = "PKU,CXL.";//Z(90) < a(97)
    //char* p2 = "";

    char* p2 = "Welcome to BIU.";
    int ret = my_strncmp(p1, p2, 6);
    printf("%d\n", ret);
    return 0;
}

    char* p = '\0'; 和 char* p = ""; 的区别在于类型和语义:
    1.类型与合法性


    char* p = '\0';
    '\0' 是字符常量,类型为 int(值为 0 )。将 int 直接赋值给指针 p 在 C 语言中
    是不合法的(除非通过强制类型转换),编译器通常会报警告或错误。
    此语句意图可能是将 p 设为空指针,但正确写法应为 char* p = NULL; 或 char* p = (char*)0;。

    char* p = "";
    "" 是空字符串字面量,类型为 const char[1](包含一个 '\0' 字符)。
    将字符串地址赋值给 p 是合法的,p 指向静态存储区中的空字符串。

    2.语义与行为


    char* p = '\0';
    若强制编译通过(如 char* p = (char)'\0'; ),则 p 的值是 0(即空指针)。
    解引用 p(如 *p)会导致未定义行为(通常是段错误)。

    char* p = "";
    p 指向一个有效内存地址,存储内容为 '\0'。解引用 p(如 *p)是安全的,值为 '\0'。

    3.对比

    // char* p = '\0'; (错误用法)
    p->NULL(空指针,不可访问)

    // char* p = "";
    p ->[内存地址] -> "\0" (有效字符串)

    总结


    char* p = '\0';是错误的类型转换,可能导致空指针问题。
    char* p = "";正确初始化指针,指向合法的空字符串。

    Return Value

    Returns an integral value indicating the relationship between the strings :

    return value    indicates
    <0           the first character that does not match has a lower value in str1 than in str2
    0            the contents of both strings are equal
    >0           the first character that does not match has a greater value in str1 than in str2

/* strncmp example */
#include <stdio.h>
#include <string.h>

int main()
{
    char str[][5] = { "R2D2" , "C3PO" , "R2A6" };
    int n;
    puts("Looking for R2 astromech droids...");
    for (n = 0; n < 3; n++)
        if (strncmp(str[n], "R2xx", 2) == 0)
        {
            printf("found %s\n", str[n]);
        }
    return 0;
}

1.8 strstr

    字符串里找另一个字符串

    char* strstr(const char* str1, const char* str2);

    规则:

        找到了,返回这个字符串第一次出现的位置;找不到返回空指针。

#include<stdio.h>
#include<string.h>

int main()
{
    char arr1[] = "Chen Xianglin,PKU,2026.";
    char arr2[] = "Chen Xianglin";
    char* p = strstr(arr1, arr2);
    if (p == NULL)
        printf("Not Found.\n");
    else
    {
        printf("%s\n", p);
        printf("Its addition is %p.\n", p);
    }
    return 0;
}

如果找到了,要打印找到的字符串时,会从找到的位置开始,一直打印到 '\0' 结束。
              要打印地址时,会打印找到该字符串的首字母的位置。

strstr的模拟实现

    思路:
    情况①
    arr1[]——abcdefg
    arr2[]——bcd
    情况②
    arr1[]——abbbcdefg
    arr2[]——bbc
    情况③
    arr2[]——'\0'  为空串

方法①

#include<assert.h>
char* my_strstr(const char* p1, const char* p2)
{
    assert(p1 && p2);
    if (*p2 == '\0')
        return (char*)p1;
  
 //为了后面更方便循环遍历和返回指针,故再次创建几个指针。
    const char* s1 = p1, *s2 = p2;//在不改变 p1,p2 的情况下,而建立的用于遍历对比的指针。
    const char* cp = p1;//arr1每次循环遍历的新起点
    while (*cp != '\0' && *s2 != '\0')
    {
        s1 = cp, s2 = p2;
        while (*s1 == *s2)
        {
            s1++, s2++;
        }
        if (*s2 != '\0')
            cp++;
    }
    if (*cp == '\0')
        cp = NULL;
    return (char*)cp;
}
int main()
{
    char arr1[] = "Chen Xianglin,PKU,2026.";//asssdfgh
    char arr2[] = "Chen Xianlin";//ssd
    char* p = my_strstr(arr1, arr2);
    if (p == NULL)
        printf("Not Found.\n");
    else
    {
        printf("%s\n", p);
        printf("Its addition is %p.\n", p);
    }
    return 0;
}

方法②

#include<assert.h>
const char* my_strstr(const char* p1, const char* p2)
{
    assert(p1 && p2);
    if (*p2 == '\0')
        return (char*)p1;
    const char* s1 = p1, * s2 = p2;
    const char* cp = p1;
    while (*cp)
    {
        s1 = cp, s2 = p2;
        while (*s1 == *s2 && *s1 != '\0' && *s2 != '\0')
        {
            s1++, s2++;
        }
        if (*s2 == '\0')
            return (char*)cp;
        cp++;
    }
  
 //跳出外层while时,*cp一定等于'\0'
    return NULL;
}
int main()
{
    char arr1[] = "Chen Xianglin,PKU,2026.";
    char arr2[] = "Chen Xianglin";
    char* p = my_strstr(arr1, arr2);
    if (p == NULL)
        printf("Not Found.\n");
    else
    {
        printf("%s\n", p);
        printf("Its addition is %p.\n", p);
    }
    return 0;
}

方法③

#include<assert.h>
char* my_strstr(const char* p1, const char* p2)
{
    assert(p1 && p2);
    if (*p2 == '\0')
        return (char*)p1;
    const char* s1 = p1, * s2 = p2;
    while (*p1)
    {
        s1 = p1, s2 = p2;
        while (*s1 == *s2 && *s1 != '\0' && *s2 != '\0')
        {
            s1++, s2++;
        }
        if (*s2 == '\0')
            return (char*)p1;
        p1++;
    }
    return NULL;
}
int main()
{
    char arr1[] = "Ch";
    char arr2[] = "hee";
    char* p = my_strstr(arr1, arr2);
    if (p == NULL)
        printf("Not Found.\n");
    else
    {
        printf("%s\n", p);
        printf("Its addition is %p.\n", p);
    }
    return 0;
}

方法④

char* __cdecl strstr(const char* str1, const char* str2)
{
    char* cp = (char*)str1;
    char* s1, * s2;

    if (!*str2)
      
 //!*str2
        //    !是逻辑非运算符,会将结果取反。
        //    如果* str2 的值是 '\0'(ASCII 值为 0),则 !*str2 等价于 !0,结果为 `1`(条件为真)。
        //    如果 * str2 是非空字符(如 'a'),则 !*str2 等价于 !非零值,结果为 0(条件为假)。

        return((char*)str1);
    while (*cp)
    {
        s1 = cp;
        s2 = (char*)str2;
        while (*s2 && !(*s1 - *s2))
            s1++, s2++;
        if (!*s2)
            return(cp);
        cp++;
    }
    return(NULL);
}

方法⑤——KMP算法

Returns a pointer to the first occurrence of str2 in str1, or a null pointer if str2 is not part of
str1.

/* strstr example */
#include <stdio.h>
#include <string.h>

int main()
{
    char str[] = "This is a simple string";
    char* pch;
    pch = strstr(str, "simple");
    strncpy(pch, "sample", 6);
    puts(str);
    return 0;
}

strstr的应用

#include<stdio.h>
#include<string.h>

int main()
{
    char arr1[] = "Christophe Chen, Chen Xianglin, welcome to Peking University.";
    char arr2[] = "Chen Xianglin";
    char* ret = strstr(arr1, arr2);
    printf("%s\n", ret);
    return 0;
}

    strstr的模拟实现——KMP算法版

    #include<stdio.h>
    #include<string.h>
    #include<assert.h>
    #include<stdlib.h>

    void GetNext(char* sub, int* next, int lenSub)
    {
        assert(sub && next);
        next[0] = -1, next[1] = 0;
        int i = 2, k = 0;
        while (i < lenSub)
        {
            if (k == -1 || sub[k] == sub[i - 1])
                next[i] = k + 1, i++, k++;
            else
                k = next[k];
        }
    }
    char* my_strstr(char* str, const char* sub)
    {
        assert(str && sub);
        int lenStr = (int)strlen(str), lenSub = (int)strlen(sub);
        int* next = (int*)malloc(sizeof(int) * lenSub);
        assert(next);
        GetNext(sub, next, lenSub);
        int i = 0, j = 0;
        while (i < lenStr && j < lenSub)
        {
            if (j == -1 || str[i] == sub[j])
                i++, j++;
            else
                j = next[j];
        }
        if (j >= lenSub)
            return &str[i - j];
        return NULL;
    }
    int main()
    {
        char arr1[] = "Christophe Chen, Chen Xianglin, welcome to Peking University.";
        char arr2[] = "Chen Xianglin";
        char* ret = my_strstr(arr1, arr2);
        printf("%s\n", ret);
        return 0;
    }

1.9 strtok

    sep参数是个字符串,定义了用作分隔符的字符集合
    第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的
   标 记。

    char* strtok(char* str, const char* sep);

    strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:
    strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内
    容并且可修改。)
    strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字
    符串中的位置。
    strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标
    记。如果字符串中不存在更多的标记,则返回 NULL 指针。

/* strtok example */
#include <stdio.h>
#include <string.h>

int main()
{
    char str[] = "- This, a sample string.";
    char* pch;
    printf("Splitting string \"%s\" into tokens:\n", str);
    pch = strtok(str, " ,.-");
    while (pch != NULL)
    {
        printf("%s\n", pch);
        pch = strtok(NULL, " ,.-");
    }
    return 0;
}

#include <stdio.h>
int main()
{
    char* p = "zhangpengwei@bitedu.tech";
    const char* sep = ".@";
    char arr[30];
    char* str = NULL;
    strcpy(arr, p);
//将数据拷贝一份,处理arr数组的内容
    for (str = strtok(arr, sep); str != NULL; str = strtok(NULL, sep))
    {
        printf("%s\n", str);
    }
}

用法:

方法一:

int main()
{
    char mail[] = "chenxianglinpekinguniversity@master.net";
    char* p = "@.";
    char buf[60] = { 0 };
    strcpy(buf, mail);
  
 //因为strtok会修改原字符串(会将分隔符改为'\0'),因此,让strcpy拷贝一个临时可供修改的字符串
    char* ret = strtok(buf, p);
    printf("%s\n", ret);
//chenxianglinpekinguniversity
    ret = strtok(NULL, p);
    printf("%s\n", ret);
    ret = strtok(NULL, p);
    printf("%s\n", ret);
    return 0;
}

方法二:

int main()
{
    char mail[] = "chenxianglinpekinguniversity@master.net";
    char* p = "@.";
    char buf[60] = { 0 };
    strcpy(buf, mail);
    char* ret = NULL;
    for (ret = strtok(buf, p); ret != NULL; ret = strtok(NULL, p))
    {
        printf("%s\n", ret);
    }
    return 0;
}


注意:

'\0'作为字符串的结束符一般是不能用来作为分隔符的。

http://www.xdnf.cn/news/763165.html

相关文章:

  • 上位机知识篇---直接无线数据通信
  • 线程与进程
  • 【Dv3Admin】系统视图用户管理API文件解析
  • Redis 架构设计
  • Kali Linux从入门到实战:系统详解与工具指南
  • Fréchet Inception Distance(FID)
  • ubuntu系统更换镜像源
  • [GESP202412 五级] 奇妙数字
  • java 多线程中的volatile关键字作用
  • 【JavaScript-Day 28】告别繁琐循环:`forEach`, `map`, `filter` 数组遍历三剑客详解
  • c++之循环
  • java CountDownLatch‌
  • 题海拾贝:压缩字符串
  • 详解鸿蒙开发如何上传三方库到ohpm仓库
  • 【Dv3Admin】系统视图系统配置API文件解析
  • 017搜索之深度优先DFS——算法备赛
  • java ExecutorService线程池使用(ExecutorService/Completable异步+ExecutorService线程池)
  • Office安装包2024版
  • ck-editor5的研究 (4):初步使用 CKEditor5 的插件功能
  • 72.编辑用户消息功能之前端实现
  • PCB制作入门
  • 开始通信之旅-----话题通信
  • 关于 java:4. 异常处理与调试
  • C#数字图像处理(二)
  • IO流1——体系介绍和字节输出流
  • 如何用利用deepseek的API能力来搭建属于自己的智能体-优雅草卓伊凡
  • 【AI面试秘籍】| 第25期:RAG的关键痛点及解决方案深度解析
  • OpenGL、GLUT、freeGLUT 与 GLFW 的区别
  • 【渲染】拆解《三国:谋定天下》场景渲染技术
  • C++实现汉诺塔游戏自动完成