【c基础】常用字符串库函数

一、常用字符串库函数

(1)gets和fgets函数

  • gets(char *s); //参数是一个字符数组 //作用就像scanf一样,并且不会被空格断开
  • gets认为回车代表输入完成,空格只是字符串中的一部分而已
  • gets和scanf一样有缓冲区溢出的危险,使用gets和scanf都要注意缓冲区溢出的问题
  • fgets是安全的,因为它改进了gets内存溢出的问题,不会因为用户恶意的输入过长的字符串导致溢出(当输入个数过多时,取前面几个,比如10个长度的字符串,会取输入的前9个字符)
  • fgets函数是为读取文件而设计的,所以读取键盘时灭有gets那么方便
  • fgets有三个参数,第一个参数是char的数组,第二个参数是标明这个数组的大小,第三个
    参数输入来源如果是从键盘输入可以固定写为stdin。fgets(char *s, size_t len, stdin)
  • fgets会认为用户输入的回车也是字符串的一部分内容。回车同时也是结束输入的标志。
#include <stdio.h>

int main()
{
    char a[10] = {0};
    fgets(a, sizeof(a), stdin);
    printf("(%s)\n", a);
    
    return 0; 
}

运行结果

[root@localhost test]# gcc main.c -o main
[root@localhost test]# ./main
hello
(hello
)
[root@localhost test]# ./main
hello worldxxx
(hello wor)
[root@localhost test]# 

(2)puts和fputs函数

  • puts打印字符串,与printf不同,puts会在最后自动加一个'\n',并且puts不支持各种转义字符,比如%d,%s都不支持,puts只能简单的直接输出一个字符串,而不能输出char, int,double等其他类型。
  • puts的底层函数是putchar,函数原型为【int puts(const char *s);】
  • fputs是puts的文件操作版本。第一个参数是char的数组,第二个参数是输出端,如果只是在屏幕输出的话可以固定为stdout。
  • fgets不会自动输出一个\n。
#include <stdio.h>

int main()
{
    char s[] = "hello world";
    puts(s);
    fputs(s, stdout);
    
    return 0; 
}

(3)strlen函数

  • 返回字符串长度,返回不包含字符串结尾'\0'的长度,实际上返回的是这个字符串实际占用的字节数。注意一个汉字在gbk模式下占用2字节,在utf-8模式下占用3字节。
  • 原型:size_t strlen(const char *_Str);
int main()
{   
    int len;
    char s[100] = "hello world";
    len = strlen(s);
    printf("%d\n", len);  // 11

    char s2[100] = "hello好好学习";
    len = strlen(s2);
    printf("%d\n", len);  // gbk编码时:13  utf8编码时:17
    
    return 0; 
}

(4)strcat和strncat函数

size_t strcat(char *_Str1, const char *_Str2)
将参数Str2追加到Str1后面
用strcat的时候要注意,第二个字符串过长时,第一个字符串会自动扩展空间
#include <stdio.h>
#include <string.h>

int main()
{   
    char a[10] = "abxxc";
    char b[10] = "de";
    strcat(a, b);
    printf("%s\n", a); //abxxcde

    //注意当a的空间不够时,会自动扩展长度
    char c[10] = "fghi";
    strcat(a, c);
    printf("%s\n", a); //abxxcdefghi
    
    return 0; 
}
size_t strncat(char *_Str1, const char *_Str2, size_t len)
字符串有限追加,在strcat的基础上增加了一个参数,这个参数来表明最多可以追加str2的前几个char
#include <stdio.h>
#include <string.h>

int main()
{   
    char a[10] = "abxxc";
    char b[10] = "defhgi";

    strncat(a, b, 5);
    printf("%s\n", a); // abxxcdefhg
    
    return 0; 
}

(5)strcmp和strncmp函数

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

该函数主要是用来实现字符串对比,该函数执行为将字符串str1和str2进行对比,不能将两个字符串变量名直接比较,因为相当于比较两个地址值。

  • 如果str1>str2则返回一个正数
  • 如果str1<str2则返回一个负数
  • 如果str1=str2则返回0
从第一个字符开始比较,若相同则判断下一个字符,若不等则返回结果,str1该位置字符大,则返回正数,若小返回负数;若str1和str2长度相等且每个字符都相等,则返回0;若str1和str2长度不相等,且较短的那个字符串是较长字符串的最左边一部分,则较长的那个字符串大,即此时若str1较长,则返回正数,若str2较长,则返回负数。
int strcmp(const char *_Str1, const char *_Str2, size_t n);

比较两个字符串前n个字符是否相等,相等返回0,不等返回非0(一般是1);

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

int main()
{   
    char time1[] = "09:30:00";
    char time2[] = "09:31";
    char time3[] = "09:30:001";

    int ret;
    ret = strcmp(time1, time2);
    printf("%d\n", ret);  // -1  // 09:30:00 < 09:31

    ret = strcmp(time1, time3);
    printf("%d\n", ret);  // -49 // 09:30:00 < 09:30:00x

    ret = strncmp(time1, time3, 8);
    printf("%d\n", ret);  // 0
    
    return 0; 
}

(6)strcpy和strncpy函数

字符串拷贝:注意strcpy是直接将str2覆盖掉str1,而strncpy是将_Str2将n个字符覆盖掉_Str1前n个字符,如果_Str1本来的字符长度大于n,则覆盖后n后面的字符不变
//将参数_Str2拷贝到_Str1中
char *strcpy(char *_Str1, const char *_Str2);

//将参数_Str2拷贝到_Str1中, 拷贝前n个
char *strncpy(char *_Str1, const char *_Str2, size_t n); 

示例

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

int main()
{   
    char time1[] = "09:30:00";
    char time2[] = "09:31";
    char time3[] = "09:30:001";

    strcpy(time1, time2);
    printf("%s\n", time1);  // 09:31

    strncpy(time3, time2, 5);
    printf("%s\n", time3);  // 09:31:001

    
    return 0; 
}

(7)sprintf和sscanf函数

  • 和printf函数功能类似,printf将格式化结果输出到屏幕,sprintf将格式化结果输出到字符串
  • sprintf和printf用法类似,唯一区别在于增加了第一个参数,第一个参数是char数组
  • 所有printf的转移符对于sprintf是一样
#include <stdio.h>
#include <string.h>

int main()
{   
    char a[100];
    sprintf(a, "%s\n", "(hello world)");
    printf("%s", a); //(hello world)

    sprintf(a, "( %d%s)\n", 3, "yourname");
    printf("%s", a); //( 3yourname)

    sprintf(a, "(%s)\n", "xxx");
    printf("%s", a); //(xxx)

    return 0; 
}
  • sscanf类似于scanf函数,scanf从键盘读取输入,sscanf从指定格式化字符串读取输入
  • sscanf多了第一个参数,char的数组,sscanf会从这个数组中读取相关的内容。用sscanf传入字符串到指定数组后,那个数组的每一个字符都是字符串的每一个字符,当数组长度比字符串长时,数组后面的元素为0
  • 注意以下示例代码中的&符号容易忘
#include <stdio.h>
#include <string.h>

int main()
{
    char a[100] = "hello56+k+72name";
    int i, j;
    sscanf(a, "hello%d+k+%dname", &i, &j); //从字符串里面取数
    printf("i = %d, j = %d\n", i, j); //i = 56, j = 72
    
    return 0;
}

(8)strchr和strstr函数

  • 【char * strchr(char * _Str, int _Ch);】  在参数_Str中查找参数 _Ch指定字符,找到返回字符 _Ch在_Str中的所在地址,没有找到返回 NULL
  • 【char * strstr(const char *_Str, const char *_SubStr);】  在参数_Str中查找参数_SubStr指定子串,找到返回子串_SubStr在_Str中的所在首地址,没有找到返回NULL
示例代码
#include <stdio.h>
#include <string.h>

int main()
{
    char a[100] = "hello world";
    char *s; //定义了char类型的指针变量

    s = strchr(a, 'l'); //返回l所在的地址
    printf("%s\n", s);  //llo world

    s = strchr(a, 'w'); //返回w所在的地址
    printf("%s\n", s); //world

    s = strchr(a, 'a'); //返回a所在的地址
    if (s == NULL)
        printf("s is NULL, not find\n");
    else
        printf("%s, \n", s);

    s = strstr(a, "wo"); //返回w子串的首地址
    printf("%s\n", s); //world

    return 0;
}

(9)strtok 字符串分割函数

字符在第一次调用strtok()时必须给予参数s字符串,往后的调用则将参数s设置为NULL,每次调用成功则返回指向被分割片段的首地址
示例代码
#include <stdio.h>
#include <string.h>

int main()
{
    char a[100] = "abc_3434_xxxxx_world";
    char *s; //定义一个char的指针变量

    // 1.一个一个取
    s = strtok(a, "_"); //注意是"_"不是'_' //"_"就是分割符
    printf("%s\n", s); // abc

    s = strtok(NULL, "_"); //如何取出第二个
    printf("%s\n", s); // 3434

    s = strtok(NULL, "_"); //取出第三个
    printf("%s\n", s); // xxxxx

    s = strtok(NULL, "_"); //取出第四个
    printf("%s\n", s); // world

    s = strtok(NULL, "_"); //取出第五个
    if (s == NULL){printf("s is NULL\n", s);}
    

    // 2.一次性取出所有的
    char b[100] = "abc_3434_xxxxx_world";
    s = strtok(b, "_");
    while (s != NULL)
    {
        printf("%s\n", s);
        s = strtok(NULL, "_");
    }
    
    return 0;
}

运行结果

[root@localhost test]# gcc main.c -o main
[root@localhost test]# ./main
abc
3434
xxxxx
world
s is NULL
abc
3434
xxxxx
world
[root@localhost test]# 

(10)atoi和atof和atol

  • atoi的功能就是把一个char的数组转化为一个int,需要头文件stdlib.h。参数传递一个指针或者地址
  • atof,把一个小数形式的字符串转化为一个浮点数float
  • atol,将一个字符串转化为long类型
  • 在c语言里面提供了把字符串转化为整数的函数,但并没有提供把整数转化为字符串的函数
  • c语言并没有把一个int转化为字符串的函数,所以不要尝试使用itoa这种函数,可以使用sprintf将一个int,或者其他类型转化为一个字符串。
  • atoi是标准的库函数,itoa不是c语言标准的库函数。
  • itoa可以在vs编译,但在其它地方就不知道了
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main()
{
    char a[100] = "123";
    int a1 = atoi(a);
    printf("%d\n", a1); //123

    char b[100] = "123.1";
    float b1 = atof(b);
    printf("%f\n", b1); //123.099998

    long a2 = atoi(a);
    printf("%d\n", a2); //123

    //用sprintf将int转化为字符串
    char str1[] = "abc";
    int num = 45;
    sprintf(a, "%d", num);
    printf("%s\n", a);  // 45

    strcat(str1, a);
    printf("%s\n", str1); //abc45

    return 0;
}

(11)memset、memcpy和memmove

①memset 内存值设置

需要包含头文件#include <string.h>
void *memset(void *s, int c, size_t n);
memset的功能主要是设置一块内存区域的值重新为多少,第一个参数是内存首地址,第二个参数是要设置的值,第三个参数设置多少内存(单位字节)为那个值。
第一个参数一般是指定要置空内存的首地址,第二个参数一般写0,第三个参数是这块内存的 大,这种设置表示将这块内存的值全部重新设置为0。
#include <stdio.h>
#include <string.h>

int main()
{
    int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    // 将数组内容全部改为0
    // 1.传统方法:遍历每个成员,将其值改为0
    // 2.用memset函数
    memset(a, 0, sizeof(a));
    for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
    {
        printf("a[%d] = %d\n", i, a[i]);
    }
    
    return 0;
}

②memcpy 内存值拷贝

void *memcpy(void *dest, const void *src, size_t n); //内存拷贝
第一个参数是目标内存首地址,第二个参数是源内存首地址,第三个参数是拷贝字节数

使用memcpy时,一定要确保内存没有重叠区域

同类型拷贝
#include <stdio.h>
#include <string.h>

int main()
{
    int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    int b[10] = {0};
    //内存拷贝,将a的前n个字节拷贝到b的前n个字节
    // 1.传统方法:遍历,然后赋值
    // 2.用memcpy函数
    memcpy(b, a, 8); //拷贝前8个字节,这里也就是2个int
    for (int i = 0; i < 10; i++)
    {
        printf("b[%d] = %d\n", i, b[i]);
    }
    /*
    b[0] = 1
    b[1] = 2
    b[2] = 0
    b[3] = 0
    b[4] = 0
    b[5] = 0
    b[6] = 0
    b[7] = 0
    b[8] = 0
    b[9] = 0
    */

    return 0;
}
不同类型拷贝
#include <stdio.h>
#include <string.h>

int main()
{
    //将short拷贝到int,结果会是怎样
    short a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    int b[10] = {0};

    printf("%p, %p\n", &a[0], &a[1]); // 0x7fff7bf088b0, 0x7fff7bf088b2
    // 可以看到a[0]的地址比a[1]的地址小

    printf("%d\n", sizeof(a));  //20

    memcpy(b, a, sizeof(a)); //拷贝前20个字节
    /*
    由于两个short等于一个int
    因此b中的值是a中两个short在内存中拼接后的值
    以b[0]为例,它由a[0]和a[1]的补码拼接而成
    a[0]: 1 → short补码=原码 00000000 00000001
    a[1]: 2 → short补码=原码 00000000 00000010

    小端对齐,即低地址对应低位数,因此1和2的补码拼接起来
                00000000 00000010 00000000 00000001  
    ->对应16进制: 0   0    0   2    0   0    0   1
    打印显示时不展示前面的0

    同理b[1]由a[2]和a[3]的补码拼接而成
    a[2]: 3 → short补码=原码 00000000 00000011
    a[3]: 4 → short补码=原码 00000000 00000100
    小端对齐,低地址对应低位数,拼接
         00000000 00000100 00000000 00000011
    对应16进制   0  0  0 4 0 0  0 3
    */
    for (int i = 0; i < 10; i++)
    {
        // 按16进制输出
        printf("b[%d] = %x\n", i, b[i]);
    }
    /*
        b[0] = 20001
        b[1] = 40003
        b[2] = 60005
        b[3] = 80007
        b[4] = a0009
        b[5] = 0
        b[6] = 0
        b[7] = 0
        b[8] = 0
        b[9] = 0
    */
    return 0;
}
内存重叠时拷贝要注意
#include <stdio.h>
#include <string.h>

int main()
{
    int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    memcpy(&a[3], &a[0], 20); //拷贝5个int //注意存在内存重叠
    for (int i = 0; i < 10; i++)
    {
        printf("a[%d] = %d\n", i, a[i]);
    }
    /*
    a[0] = 1
    a[1] = 2
    a[2] = 3
    a[3] = 1
    a[4] = 2
    a[5] = 3
    a[6] = 4
    a[7] = 5
    a[8] = 9
    */

    return 0;
}

③memmove 内存移动

void *memmove(void *dest, const void *src, size_t n); //内存移动,参数与memcpy一致

作用几乎与memcpy,都会进行拷贝。只是在存在重叠区域时的处理有所不同:

  • `memcpy()` 假设源内存区域和目标内存区域之间没有重叠。如果这两个区域确实存在重叠,`memcpy()`的行为是未定义的,这意味着它可能会拷贝错误的数据或者破坏源数据。因此,使用`memcpy()`时,程序员需要确保源和目标内存区域不重叠
  • `memmove()` 考虑到源内存区域和目标内存区域之间可能存在的重叠。如果存在重叠,`memmove()`会采取适当的措施来确保数据被正确拷贝。这通常涉及到先拷贝数据到一个临时位置,然后再从临时位置拷贝到目标位置,从而避免数据被意外覆盖。
#include <stdio.h>
#include <string.h>

int main()
{
    int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    int b[10] = {0};

    // 内存移动
    // 
    memmove(b, a, sizeof(a));

    for (int i = 0; i < 10; i++)
    {
        printf("a[%d] = %d, b[%d] = %d\n", i, a[i], i, b[i]);
    }

    /*
    a[0] = 1, b[0] = 1
    a[1] = 2, b[1] = 2
    a[2] = 3, b[2] = 3
    a[3] = 4, b[3] = 4
    a[4] = 5, b[4] = 5
    a[5] = 6, b[5] = 6
    a[6] = 7, b[6] = 7
    a[7] = 8, b[7] = 8
    a[8] = 9, b[8] = 9
    a[9] = 10, b[9] = 10
    */

    return 0;
}

二、字符串内容解析

(1)解析单个运算表达式

有一个字符数组,整数是任意的,中间可能是* + - /

比如char a[100] = "43-56="

写一个程序,将结算的结果追加到字符串a的后面

程序执行完成后a的值是"43-56=-13"

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

int main()
{
    char a[10] = "43-56=";
    int i, j;
    char s;

    // for (i = 0; i < sizeof(a); i++)
    // {
    //     printf("a[%d] = %c\n", i, a[i]);
    // }

    //用%c取char
    sscanf(a, "%d%c%d=", &i, &s, &j);
    printf("%d%c%d\n", i, s, j);

    //用switch来进行运算符号的判定
    //因为用if语句会出问题,比如if (s == "-")这里的s是ASCII码值
    int result = 0;
    // if (s == "-")
    // {
    // printf("hellow world");
    // }
    switch (s)
    {
    case '+':
        result = i + j;
        break;
    case '-':
        result = i - j;
        break;
    case '*':
        result = i * j;
        break;
    case '/':
        result = i / j;
        break;
    default:
        result = 0;
    }

    sprintf(a,"%d%c%d=%d", i, s, j, result);
    printf("%s\n", a);
    
    return 0;
}

(2)解析多个运算表达式

char a[100] = "12+5=;45-2=;34*2=;56/3=;

到底有多少分号是不确定的

写个程序,执行后=号后面自动添加计算结果

“12+5=17;45-2=43;34*2=68;56/3=18”

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

int main()
{
    char a[100] = "12+5=;45-2=;34*2=;56/3=";
    // 1.用strtok将;部分拆开
    char *s;
    int i, j;
    char operation;
    int result;

    char temp[sizeof(a)];
    char a2[sizeof(a)] = ""; //初始为空字符串

    s = strtok(a, ";");
    while (s != NULL)
    {
        // 2.用sscanf获取每个数字和运算符
        printf("%s\n", s);
        sscanf(s, "%d%c%d=", &i, &operation, &j);

        // 3.用switch计算结果
        switch (operation)
        {
        case '+':
            result = i + j;
            break;
        case '-':
            result = i - j;
            break;
        case '*':
            result = i * j;
            break;
        case '/':
            result = i / j;
            break;
        default:
            result = 0;
        }

        s = strtok(NULL, ";");

        // 4.用sprintf得到指定格式的字符串
        // printf("%d%c%d=%d\n", i, operation, j, result);
        if (s != NULL)
        {
            sprintf(temp, "%d%c%d=%d;", i, operation, j, result);
        }
        else
        {
            sprintf(temp, "%d%c%d=%d", i, operation, j, result);
        }

        // 5.用strcat拼接字符串
        strcat(a2, temp);
    }

    strcpy(a, a2);
    printf("%s\n", a); // 12+5=17;45-2=43;34*2=68;56/3=18
    
    return 0;
}

  • 21
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值