字符串函数(超详细)

       C语言中有很多库函数,其中我们有必须知道一些函数的使用,其中比较典型是字符串函数,内存函数,和动态内存函数。这篇文章我们就来了解字符串函数。使用字符串函数要引用string.h头文件。

目录

字符串函数

1.strlen函数

2.strcpy函数

​3.strcat函数

4.strcmp函数

5.strncat函数

6.strncpy函数 

7.strncmp函数

8.strstr函数 

9.strtok函数 

10.strerror函数

11.其他字符传函数 

字符串函数

1.strlen函数

       这是我们最熟悉的一个求字符串长度的函数。我们先看C语言里面它的标准声明。       C定义size_t是无符号整数(unsigned int),我们会找字符串结束的标志'\0',之后返回无符号整数。我们来模拟实现这个函数。

//size_t == unsigned int
int my_strlen(const char* str)
{//const修饰指针里面的变量,指针指向的内容不能通过解引用改变
    int count = 0;
    assert(str != NULL);
    while (*str != 0)//==while(*str)
    {
        count++;
        str++;
    }
    return count;
}
int main()
{
    //int len = my_strlen("abcdef");
    //printf("%d\n", len);
    //       3     -    6=-3(无符号数运算结果还是无符号数)
    if (strlen("abc") - strlen("abcdef") > 0)
    {//strlen返回类型是unsignde int
        printf("hehe\n");
    }
    else
    {
        printf("haha\n");
    }
    return 0;
}

       这里打印的是“hehe”因为无符号运算无论如何结果都是大于0的,所以打印hehe.

       我们还可以通过递归的方法来实现:

int my_strlen(const char* str)
{
	if (*str == '\0')
	{
		return 0;
	}
	else
	{
		return 1 + my_strlen(str + 1);
	}
}

int main()
{
	//递归的方式实现 strlen
	char arr[] = "abcd";
	size_t len = my_strlen(arr);
	printf("len = %zd\n", len);
	return 0;
}

       当读到最后一个字符‘\0’时返回0。这里不再做过多解释。

2.strcpy函数

       拷贝字符串函数,先传入要改变的字符串,再传入要拷贝的内容。返回的值是改变字符串的首元素地址。一定牢记,字符串结束的标志是'\0'。所以只需要将复制的字符串拷贝到'\0'即可,后面即使没有变也不需要读取。

       我们先来看标准定义:

int main()
{
	char arr1[20] = { 0 };
	char arr2[] = "hello";
	strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

       目标空间必须确保足够大,能放的下拷贝的字符串。但是有一种情况,就是注意指针可能指向常量字符串,这样空间是不能被修改的。

        接下来我们模拟实现strcpy. 


 char* my_strcpy(char* dest,const char* src)
{//使src是常量字符串
    assert(dest != NULL);
    assert(src != NULL);
    char* ret = dest;
    //ret指向dest的首位置
    //while (*src)
    //{
    //    *dest++ = *src++;
    //}
    //*dest = *src;
    while (*dest++ = *src++)
    {//最后直接把'\0'赋给dest
        ;
    }
    //返回目的空间的起始地址
    return ret;
}
int main()
{
    //错误示范
    //char*p="abcdef"是常量字符串内容不可改
    //arr1[]="ab"会崩
    char arr1[] = "abcdefghi";
    char arr2[] = "bit";
    my_strcpy(arr1, arr2);
    printf("%s\n", arr1);
    return 0;
}

3.strcat函数

       追加字符串函数,先传入在前面的字符串,再传入在后面的字符串,返回的是最前面元素的首地址。       一样的,也是要确保目标字符串内存够大。我们先来看看目标空间不足的情况:

int main()
{
    char a1[] = "hello";
    char a2[] = "world";
    strcat(a1, a2);
    printf("%s\n", a1);
    return 0;
}

       所以空间一定要够。 

int main()
{
    char a1[30] = "hello\0xxxxxxx";
    char a2[] = "world";
    strcat(a1, a2);
    printf("%s\n", a1);
    return 0;
}

        使用库函数strcat不要给自己追加,否则会崩溃。       

     那这个情况该如何解决?放心,C语言考虑的很全面,会有办法,我们一会再讲。  

     我们再来模拟实现一下: 

char*my_strcat(char* dest, const char* src)
{
    assert(dest && src);
    char* ret = dest;
    //1.找到目的字符串的'\0'
    while (*dest != '\0')
    {
        dest++;
    }
    //2.追加
    while (*dest++ = *src++)
    {
        ;
    }
    return ret;
}
int main()
{
    char a1[30] = "hello";
    char a2[] = "world";
    my_strcat(a1, a2);
    printf("%s\n", a1);
    return 0;
}

       先在原字符串找到‘\0’,之后令它们++相等。

4.strcmp函数

       比较字符串函数,从第个开始比较,之后比较下一个,一个一个的比较,直到比较完,如果都相同就返回0;前者>后者返回1(不同编译器返回值不同,可能是两个数的ASCII值的差);前者<后者返回-1(可能是两个值的ASCII值的差)。


int main()
{
    //VS 2020
    //>  1
    //== 0
    //<  -1

    //linux-gcc
    //> >0
    //== 0
    //< <0
    char* p1 = "abcdef";
    char* p2 = "sqwer";
    //strcmp比较的不是不是字符串长度
    //一对一对往后比较,不一样的话直接返回
    //int ret = strcmp(p1, p2);
    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");
    }
    //printf("%d\n", ret);
    return 0;
}

       我们依旧来模拟实现一下:

int my_strcmp(const char* s1, const char* s2)
{
	while (*s1 == *s2)
	{
		if (*s1 == '\0')
		{
			return 0;
		}
		s1++;
		s2++;
	}
	return *s1 - *s2;
}

int main()
{
	int ret = my_strcmp("bbq", "abcdef");
	if (ret > 0)
	{
		printf("大于\n");
	}
	else if (ret == 0)
	{
		printf("等于\n");
	}
	else
	{
		printf("小于\n");
	}
	return 0;
}

       这里是几个重要的字符串函数,我们要学会它们的基本用法。

       strcpy、strcat、strcmp长度不受限制的字符串函数。那么刚才strcat无法追加自己,我们总要解决,是不是加上指定追加的长度就可以完成?所以库函数又提供了一些长度受限的字符串函数。

       strncpy、strncat、strncmp长度受限制的字符串函数。

5.strncat函数

        传入在前的字符串,之后传入在后面的字符串,在之后传入要追加的元素个数,返回的是最前面元素的地址。


int main()
{
    char arr1[30] = "hello";
    char arr2[] = "world";
    strncat(arr1, arr2, 8);
    //最后拷贝'\0';即使追加个数大于的原字符串长度
    //就把最后的元素追加上去,之后补一个'\0'
    printf("%s\n", arr1);
    return 0;
}

       即使传入的整数>要拷贝的元素个数,最后会用‘\0’来代替。 

       我们来模拟实现strncat。

void my_strncat(char* dest, const char* str, int sz)
{
	assert(str);
	char* cur = dest;
	while (*cur)
	{
		cur++;
	}
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		*cur++ = *dest++;
	}
}

int main()
{
	char arr1[20] = "abcd";
	strncat(arr1, arr1, sizeof(char) * 7);
	printf("%s\n", arr1);
	return 0;
}

6.strncpy函数 

       和strcpy函数很像,多传入一个要拷贝字符的数量。返回的类型是整数,和strcpy返回的规律是一样的。

int main()
{
	char arr1[20] = "xxxxxxxxx";
	char arr2[] = "hello";
	strncpy(arr1, arr2, 7);
	printf("%s\n", arr1);
	return 0;
}

       我们来模拟一下: 

void my_strncpy(char* dest, const char* str, size_t sz)
{
	int i = 0;
	char* cur = str;
	for (i = 0; i < sz; i++)
	{
		if (i < strlen(cur))

		{
			*dest++ = *str++;
		}

		else
		{
			*dest++ = '\0';
		}
	}
}

int main()
{
	char arr1[20] = "xxxxxxxxxxx";
	char arr2[] = "abcd";
	my_strncpy(arr1, arr2, sizeof(char) * 6);
	printf("%s\n", arr1);
	return 0;
}

 7.strncmp函数

       和strcmp函数很像。返回的类型是整数,和strcmp返回的规律是一样的。


int main()
{
    //strncmp - 字符串比较
    const char* p1 = "abcdef";//p1指向内容不能修改
    const char* p2 = "abcqwer";
    //int ret =  strcmp(p1, p2);
    int ret = strncmp(p1, p2, 3);//比较前n个字符串
    printf("%d\n", ret);
    return 0;
}

8.strstr函数 

       判断字符串(子串)是否为另一个字符串(父串)的子串。返回类型为指针,如果不是返回NULL(空指针)。可以理解为字符串匹配函数。


int main()
{
    //strstr查找子字符串函数
    //char* strstr(const char* string,const char* strCharSet);
    //NULL -- 空指针
    //NUL/Null - '\0'
    //string 
    //Null-terminated string to search 从'\0'开始查找原字符串
    //strCharSet
    //Null-terminated string to search for 要查找的字符串

    char* p1 = "abcdefghi";
    char* p2 = "def";
    char* ret = strstr(p1, p2);
    //如果没找到就返回空指针
    //如果找到了返回的是源字符串中找到的比较字符串中首元素的地址
    if (ret == NULL)
    {
        printf("子串不存在\n");
    }
    else
    {
        printf("%s\n", ret);
    }
    return 0;
}

       我们来模拟实现strstr函数,通过暴力求解法BF算法。


//模拟实现strstr
char* my_strstr(const char* p1, const char* p2)
{
    assert(p1 && p2);
    char* s1 = p1;
    char* s2 = p2;
    char* cur = p1;
    if (*p2 == '\0')
    {//如果p2是空字符串
        return p1;
    }
    while (*cur)
    {
        s1 = cur;//如果碰到两个相同,第三个不同,则下次到第二个相同的开始
        //如:abcdddef
        //             ddef
        s2 = p2;//比较的字符串每次从头开始
        while ((*s1 != '\0')&&(*s2 != '\0')&&(*s1 == *s2))
        {
            s1++;
            s2++;
        }
        if (*s2 == '\0')
        {
            return cur;//找到子串
        }
        cur++;
    }
    return NULL;//找不到子串
}
int main()
{
    char* p1 = "abcdddefg";
    char* p2 = "ddef";
    char* ret = my_strstr(p1, p2);
    //如果没找到就返回空指针
    //如果找到了返回的是源字符串中找到的比较字符串中首元素的地址
    if (ret == NULL)
    {
        printf("子串不存在\n");
    }
    else
    {
        printf("%s\n", ret);
    }
    return 0;
}

        因为指向主串的指针不会回溯,但是指向要匹配的字符串(子串)如果不匹配就要回溯。所以我们多定义两个变量来记录原位置。

       这是最暴力的解决方式,当然有一种高效的KMP算法,以后我会发布博客,大家下去也可以探索探索。

9.strtok函数 

       这个函数很有意思,你可以理解为字符串分割函数。相当于分隔字符串函数,要传入原字符串,之后在创建一个字符串记录原字符串的分隔符,调用一次返回的是原字符串首元素的地址,这个函数会把原字符串中的分隔符改为‘\0’,所以为了不让它修改原字符串,我们先复制一份,在修改。第一次调用后会把分隔符改为‘\0’,下一次要传入一个空指针(NULL),返回的是空指针后面元素的地址。


int main()
{
    //192.168.31.121
    //192 168 31 121 - strtok
    //char* strtok(char* str,char* sep)
    //sep参数是个字符串,定义了用作分隔符的字符集合
    char arr[] = "zpw@bitedu.tech";
    char* p = "@.";
    //strtok(arr,p);
    char buf[200] = { 0 };
    strcpy(buf, arr);

    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;
}

       strtok函数找到str中的下一个标记,并将其用'\0'结尾,返回一个指向这个标记的指针(就是返回前面的地址)。

       上面这个函数写的很多余,很复杂。strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。也就是说,平常我们会先拷贝源字符串,之后对拷贝字符串进行修改。 

       这里我们没有看其内部的细节,也没有模拟实现(真的懒了),但我们大致可以猜到它里面可能使用了static修饰变量使其生命周期延长。感兴趣的同学可以下去看看源码里面是如何定义的。

int main()
{
    char arr[] = "zpw@bitedu.tech";
    char* p = "@.";
    char tmp[20] = { 0 };
    strcpy(tmp, arr);
    //zpw\0bitedu\0tech\0

    char* ret = NULL;
    
    for (ret = strtok(tmp, p); ret != NULL; ret = strtok(NULL, p))
    {
        printf("%s\n", ret);
    }
    return 0;
}

10.strerror函数

       这个函数会自动检查你是否使用错了别的函数,如果你使用函数错了,它会自动传一个数值进入这个函数(前提是你要调用它)。要引入errno.h的头文件。

       errno是一个全局错误码的变量,如果库函数执行错误,就会把对应错误码赋值到errno中,默认0就是没有错误。

int main()
{
    //C语言库函数
    // 错误码 错误信息
    //传0 - No error
    //传1 - Operation not permitted
    //传2 - No such file or directory
    //...
    //errno 是一个全局的错误码的变量
    //当C语言的库函数在执行过程中发生了错误,就会把对应的错误码赋值到errno中
    int i = 0;
    for (i = 0; i < 3; i++)
    {
        printf("%d: %s\n", i, strerror(i));
    }

    char* str = strerror(errno);//要引入errno.h的头文件
    return 0;
}

        perror就是printf + strerror函数。

11.其他字符传函数 

islower判断字符是否为小写。

isdigit判断字符是否为数字。

tolower大写字符转小写字符,若还是小写字符,则就是小写字符。

toupper小写字符转大写字符若,还是大写字符,则就是大写字符。

int main()
{
    char ch = 'w';
    //int ret=islower(ch);//判断是不是小写字母
    int ret = isdigit(ch);//判断是不是数字
    char c= tolower('q');//大写转小写字母,如果本身就是小写字母就不动
    char b= toupper('Q');//小写转大写字母
    char arr[] = "I Am A Student";
    //大写字母转小写字母
    int i = 0;
    while (arr[i])
    {
        if (isupper(arr[i]))
        {
            arr[i]=tolower(arr[i]);
        }
        i++;
    }
    printf("%s\n", arr);
    return 0;
}

       还有很多关于字符串的函数我们不可能一一去详细举例,我们也没有必要全部记住,我们需要的时候查找一下就可以。如果感觉写的还不错,请点点赞吧。

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值