字符串函数

1、strlen函数

strlen 函数的返回值是size_t,即无符号整型。这是有原因的,因为编写这个库函数的人认为,字符串的长度是不可能为负数的。这样就有可能出现以下错误:

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

为什么会打印“>”呢?

因为这个函数理应返回无符号整型,把-3变为无符号整型,就是一个正数。所以我们在自己编写strlen这个函数时,可以考虑把my_strlen函数的返回值设置为int。

编写自己的strlen函数有三种方法:

//计数器
#include<assert.h>
#include<stdio.h>
 int my_strlen(const char* str)
//和size_t相比,写int有好处,可以避免无符号数相减的错误答案。
//当然,size_t是考虑到字符串的长度不可能为负数
{
     int count = 0;
     assert(str);
     while (*str != '\0')
     {
         count++;
         str++;
     }
     return count;
}
//递归
  //如果题目要求不创建临时变量而求字符串长度,怎么办?
 //递归的方法
 int my_strlen(const char* str)
 {
     assert(str);
     if(*str != '\0')
     {
         return 1 + my_strlen(str + 1);
     }
     else
     {
         return 0;
     }
 }


//指针-指针
 //找到b的地址和\0的地址,相减。
    char *p = arr;     
    while (*p!='\0')
        p++;
    return p - arr;     

int main()
{
    char arr[] = "bit";
    int len = my_strlen(arr);
    printf("%d\n",len);
    return 0;
}

2、strcpy 函数

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

注意事项:源字符串必须以\0结束

目标字符串的空间一定要足够大,以确保能够放得下源字符串的内容

目标空间必须可修改

接下来时一个错误示范:

char* p = "abcdefghi";
char arr2[20] = "hehe";
strcpy(p,arr2);//p指向的是一个常量字符串,是不可被修改的!

那我们怎么编写自己的strcpy函数呢?

#include<assert.h>
char * my_strcpy(char* dest,const char* src)
//返回的是目标空间的起始地址
{
    char* ret = dest;//把目标首地址保存起来
    assert(dest && src);
    while (*dest++ = *src++)
    {
        ;
    }
    return ret;
}
int main()
{
    char arr1[] = "hehe\n";
    char arr2[20] = { 0 };
    my_strcpy(arr2, arr1);
    printf("%s\n", arr2);
    //printf("%s\n", my_strcpy(arr2, arr1) );
    return 0;
}

3、strcat 函数

int main()
{
    char arr1[20] = "hello ";  //此处还有一个空格,空格后才是'\0'
    char arr2[] = "world";
    strcat(arr1,arr2);
    printf("%s\n", arr1);
    return 0;
}

注意事项:

是从‘\0’开始追加的,覆盖掉目标空间的'\0',然后追加完成时要把源字符串的'\0'也追加过去。所以目标空间里一定要有'\0',这样才以知道从何处开始追加。源字符串也必须有'\0', 这样才知道该追加到什么地方。

目标空间必须可修改,必须足够大。

那我们怎么编写自己的strcat函数呢?

char* my_strcat(char* dest, const char* src)
{
    assert(dest && src);
    char* ret = dest;
    //找目标空间中的'\0'
    while (*dest != '\0')
        dest++;
    while (*dest++ = *src++)
    {
        ;
    }
    return ret;
}
int main()
{
    char arr1[20] = "hello ";
    char arr2[] = "world";
    my_strcat(arr1, arr2);
    printf("%s\n", arr1);
    return 0;
}

那么,我们可以用strcat函数对同一个字符串进行自己追加自己的操作吗?


int main()
{
    char arr1[20] = "bit";
    my_strcat(arr1, arr1);
    printf("%s\n", arr1);
    return 0;
}
//bit\0----->bitbitbitbitbitbitbitbitbit......根本找不到'\0'

不可以!会陷入死循环!因为找不到‘\0’了!

目标字符串的‘\0’被覆盖后,源字符串的'\0'也不存在了。

4、strcmp 函数

if("abcdef"=="bddsdw")
{

}

这样比较的其实是这两个字符串首字符的地址,即a和b的地址,而非内容。

所以比较两个字符串的内容时,不应该用‘==’

这个函数比较的其实是对应字符位置上的ASCLL码值大小。

那我们怎么编写自己的strcmp函数呢?

#include<stdio.h>
#include<assert.h>
//int my_strcmp(const char* str1, const char* str2)
//{
//    assert(str1 && str2 );
//    while (*str1 == *str2)
//    {
//        if (*str1 == '\0')
//            return 0;
//        str1++;
//        str2++;
//    }
//    if (*str1 > *str2)
//        return 1;
//    else
//        return -1;
//}
int my_strcmp(const char* str1, const char* str2)
{
    assert(str1 && str2);
    while (*str1 == *str2)
    {
        if (*str1 == '\0')
            return 0;
        str1++;
        str2++;
    }
    return *str1 - *str2;
}
int main()
{
    char arr1[20] = "abcdef";
    char arr2[20] = "abcdpp";
    int ret=my_strcmp(arr1, arr2);
    printf("%d\n", ret);
    return 0;
}
    if (strcpm(arr1, arr2) == 1)
        printf(">\n");

这样可以吗?不可以!因为在不同编译器上可能返回值不一样。

    if (strcpm(arr1, arr2) >0)
        printf(">\n");

至少也得是这样。

以上几种函数和长度都没关系,它们叫“长度不受限制的字符串函数”

但是这样的函数可能会出现一些不安全的情况:

int main()
{
    char arr1[] = "abcdef";
    char arr2[5] = {0};
    strcpy(arr2, arr1);//这里arr2放不下啊!程序崩溃了
    return 0;
}

5、strncpy,strncat,strncmp 函数

所以,在C语言中,又引入了一组“长度受限制的字符串函数”:

在strncpy中,要拷贝10个字符,但是实际上只有5个字符,那么它就会自动补充‘\0’,补全10个字符。

让追加几个就追加几个,但是追加完后会在最后面补充一个'\0'。

自己给自己追加的情况,用这个函数。它甚至还会在最后面放上一个'\0'。

它们和原来的字符串函数相比,多了一个参数n

6、strstr 函数

int main()
{
    char arr1[20] = "abcdefbcd";//有2个bcd,返回第一次出现的位置
    char arr2[20] = "bcd";
    char* p=strstr(arr1, arr2);
    if (p == NULL)
    {
        printf("找不到\0");
    }
    else
    {
        printf("%s\n", p);
    }
    printf("%s\n", arr1);
    return 0;
}

与之相似的函数有:

strchr 函数:找第一次出现的位置:

strrchr 函数:找最后一次出现的位置:

那么,strstr这个函数怎么实现呢?

char* my_strstr(const char* str1, const char* str2)
{
    char* s1 = NULL;
    char* s2 = NULL;
    char* cp = (char*)str1;
    while (*cp)
    {
        s1 = cp;
        s2 = (char*)str2;
        while (*s1 && *s2 && *s1 == *s2 )
        {
            s1++;
            s2++;
        }
        if (*s2 == '\0')
        {
            return cp;
        }
        cp++;
    }
    return NULL;
}

其实,用库函数,如strncmp3个3个的匹配也可以。

在字符串中查找一个字符串,还有一种效率更高的算法:

经典算法KMP算法。

感兴趣的可以自己去了解一下。

7、strtok 函数

delimiters参数是个字符串,定义了用作分隔符的字符集合

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

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

strtok函数的第一个参数不为 NULL,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。

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

如果字符串中不存在更多的标记,则返回NULL指针。

注意:要想使用NULL,必须包含头文件<stdio.h>。

//"zpengwei@yeah.net"
//@.- 分割符
//"192.168.3.212"
//"@."
int main()
{
char arr[] ="zpengwei@yeah.net";
char buf[30] ={0};//zpengwei\0yeah\Onet\0
strcpy(buf,arr);
const char* p = "@.";
char* str = strtok(buf, p);
printf("%s\n",str);

str =strtok(NULL,p);
printf("%s\n",str);

str =strtok(NULL,p);
printf("%s\n",str);

return 0;
}
//太麻烦了,能换一种方法?

//考虑循环
int main()
{
char arr[] ="192.168.3.212";
char buf[30]={0};//zpengwei\oyeah\Onet\0 
strcpy(buf,arr);

const char* p = "."; 
char* str = NULL;
for(str=strtok(buf,p);str!= NULL; str=strtok(NULL, p))
{
    printf("%s\n",str);
}
return 0;
}

这个函数应该是有记忆功能的。所以可能涉及到静态变量static

注:char * buf ="zpengwei@yeah.net";——可不可以?

当然不行,这是一个常量字符串,不可修改它的值。

8、strerror 函数

#include <string.h>
#include <stdio.h>
int main()
{
    char* p = strerror(0); 
    printf("%s\n",p);

    p = strerror(1);
    printf("%s\n", p);
    
    p = strerror(2);
    printf("%s\n",p);
    
    p = strerror(3);
    printf("%s\n",p); 
    return 0;
}

C语言的库函数在调用失败的时候,会将一个错误码放在一个叫:errno的变量中,当我们想知道调用库函数是发生了什么错误信息,就可以将:errno中的错误码翻译成错误信息。

errno函数和perror函数

#include <errno.h>

int main()
{
//打开文件
//打开文件的时候,如果文件的打开方式是"r"
//文件存在则打开成功,文件不存在打开失败
//打开文件失败的话,会返回NULL
FILE* pf = fopen("test.txt","r"); 
if(pf == NULL)
{
    printf("打开文件失败,原因是:&s\n",strerror(errno)) 
    return 1;
}
//读写文件
//...
//关闭文件 
fclose(pf); 
pf = NULL; 
return 0;
}
int main()
{
//打开文件
//打开文件的时候,如果文件的打开方式是"r"
//文件存在则打开成功,文件不存在打开失败
//打开文件失败的话,会返回NULL
FILE* pf = fopen("test.txt","r"); 
if(pf == NULL)
{
    perror("打开文件失败");
    //printf + strerror 
    return 1;}
//读写文件
//..
//关闭文件 
fclose(pf); 
pf = NULL; 
return 0;
}

前面讲解的函数都是和字符串相关的!

但是我们在操作数据的时候,不仅仅是操作字符串,那其他时候怎么做呢?

那就要提到我们的内存系列的函数了!我另外写了一篇关于内存函数的博客,感兴趣的可以去看看。

最后,简单提一下字符分类函数和字符转换函数

函数

如果它的参数符合下列条件,就返回真

iscntrl

任何控制字符

isspace

空白字符:空格',换页^f,换行"\n', 回车\r',制表符'\t'或者垂直制表符"V'

isdigit

十进制数字字符‘0’~‘9’

isxdigit

十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F

islower

小写字母a~z

isupper

大写字母A~Z

isalpha

字母a~z或A~Z

isalnum

字母或者数字,a~z,A~Z,0~9

ispunct

标点符号,任何不属于数字或者字母的图形字符(可打印)

isgraph

任何图形字符

isprint

任何可打印字符,包括图形字符和空白字符

注意:需要包含头文件<ctype.h>.

字符转换函数:

运用循环可以实现整个字符串的大小写转换。

int main()
{
    char arr[128]={0};
    gets(arr);
    int i=0;
    while(arr[i])
    {
        if(isupper(arr[i]))
        {
            arr[i]=tolower(arr[i]);
        }
        i++;
        printf("%c ",arr[i]);
    }
    return 0;
}

函数原型图片截图来源于网址:https://cplusplus.com

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值