12. 字符串

一、字符串字面量

  字符串字面量(string literal)或 字符串常量(string constant)是一个或多个字符的序列,它使用一对双引号包裹起来。编译器会自动在双引号的后面添加 '\0' 作为字符串的结束标志。

printf("hello world\n");

  从 ANSI C 标准起,如果字符串字面量之间没有间隔,或者使用空白字符分隔,C 会将其视为串联起来的字符串字面量。例如:

printf("Hello, and"" how are" "you"
    " today!\n");

与下面的代码等价:

printf("Hello, and how are you today!\n");

  字符串常量属于 静态存储类别(static storage class),这说明如果在函数中使用字符串常量,该字符串只会被储存一次,在整个程序的声明周期内存在,及时函数被调用多次。用双引号括起来的内容被视为指向该字符串储存位置的指针。

#include <stdio.h>

int main(void)
{
    printf("%s, %p, %c\n", "We", "are", *"space farets");

    return 0;
}

  printf() 根据 %s 转换说明打印 We,根据 %p 转换说明打印一个地址。因此,如果 "are" 代表一个地址,printf() 将打印该字符串首地址的地址。最后,*"space farers" 表示该字符串指向地址上存储的值,应该是字符串 *"space farers" 的首字符。

二、字符串的初始化

  在 C 语言中,我们可以使用 字符数组 来保存字符串,也就是使用一个一维字符数组保存字符串中的每一个字符。此时,编译器会自动为其添加 '\0' 作为结束符。在定义字符数组时,必须让编译器知道需要多少空间。在指定字符数组大小时,要确保数组的元素个数至少比字符串长度多 1(为了容纳空字符)。所有未被使用的元素都被自动初始化为 0(这里的 0 指的是 '\0',而不是数字字符 0)

char str[15] = "hello world!";

  我们还可以省略数组初始化声明中的大小,编译器会自动计算字符数组的大小。

char str[] = "hello world!";

  让编译器计算数组的大小只能用在初始化数组时。如果创建一个稍后再填充的数组,就必须在声明时指定大小。声明数组时,数组大小必须是可求值的整数。在 C99 新增变长数组之前,数组的大小必须是整型常量,包括有整型常量组成的表达式。

  通常,字符串都作为可执行文件的一部分储存在数据段中。当把程序载入内存是,也载入程序中的字符串。字符串储存在 静态内存区(static memory)。但是,程序在开始运行时才会为该数组分配内存。此时,才将字符串拷贝到数组中。注意,此时字符串有两个副本。一个是静态内存中的字符串字面量,另一个是储存在字符数组中的字符串。

  此后,编译器便将数组名识别为该数组首元素地址的别名。在数组形式中,数组名是 地址常量,不能更改数组名所对应的值,如果更改了数组名所对应的值,则意味着改变了数组的存储位置(即地址)。我们可以进行类似数组名 + 1 的操作,表示数组的下一个元素。

  我们也可以使用字符指针的方式来定义字符串;C 语言对字符串常量是按字符数组处理的,在内存中开辟了一个字节数组用来存放字符串常量,程序在定义字符串指针变量时只是把字符串首地址(即存放字符串的字节数组的首地址)赋给指针变量。该变量最初指向该字符串的首字符,但是它的值可以改变看。因为,可以使用递增运算符。

char * str = "hello world";

  字符串字面量被视为 const 数组。由于指针变量指向这个 const 数据,所以应该把指针变量声明为指向 const 数组的指针。这意味着不能用指针变量改变它所指向的数据,但是仍然可以改变指针变量的值(即,指针变量指向的位置)。如果把一个字符串字面量拷贝给一个数组,就可以随意改变数据,除非把这个数组声明为 const。

  字符数组是若干个元素组成的,每个元素放一个字符,而字符指针变量中存放的是地址(字符串/字符数组的首地址),绝不是将字符串放到字符指针变量中。对字符数组除了定义时可以使用字符串整体赋值,其它情况下只能单个元素赋值。对字符指针可以使用字符串整体赋值;

  如果定义了一个字符数组,那么它有确定的内存地址(即字符数组名是一个常量),而定义一个字符指针变量时,它并未指向某个确定的字符数据,并且可以多次赋值。

#include <stdio.h>

#define MSG "I'm special"

int main(void)
{
    char ar[] = MSG;
    const char * pt = MSG;

    printf("address of \"I'm special\": %p\n", "I'm special");
    printf("address ar: %p\n", ar);
    printf("address pt: %p\n", pt);
    printf("address NSG: %p\n", MSG);
    printf("address of \"I'm special\": %p\n", "I'm special");

    return 0;
}

  pt 和 MSG 的地址相同,而 ar 的地址不同。虽然字符串字面量 "I'm special" 在程序的两个 printf() 函数中出现了两次,但是编译器只使用一个储存位置,而且与 MSG 的地址相同。编译器可以把多次使用的相同字面量储存在一处或者多处。静态数据使用的内存与 ar 使用的动态内存不同。

三、字符串数组

  我们可以使用二维数组的方式或指针数组的方式创建字符串数组。

#include <stdio.h>

#define SLEN 40
#define LIM 5

int main(void)
{
    const char * mytalents[LIM] = {
        "Adding numbers swiftly",
        "Multiplying accurately",
        "Stashing data",
        "Following instructions to the letter",
        "Understanding the C language"
    };

    char yourtalents[LIM][SLEN] = {
        "Walking in a straight line",
        "Sleeping",
        "Watching television",
        "Mailing letters",
        "Reading email"
    };

    int i;

    puts("Let's compare talents.");
    printf("%-36s %-25s\n", "My Talents", "Your Talents");
    for(i = 0 ; i < LIM; i++)
    {
        printf("%-36s %-25s\n", mytalents[i], yourtalents[i]);
    }
  
    printf("\nsizeof mytalents: %zd, sizeof yourtalents: %zd\n",sizeof(mytalents), sizeof(yourtalents));

    return 0;
}

  从某些方面来看,mytalents 和 yourtalents 非常相似。两者都代表 5 个字符串。使用一个下标时都分别表示一个字符串,使用两个下标时都分别表示一个字符。而且,两者的初始化方式也相同。

  但是,它们也有区别。mytalents 数组是一个内含 5 个指针的数组,在我们的系统中共占用 40 个字节。而 yourtalents 是一个内含 5 个数组的数组,每个数组内含 40 个 char 类型的值,共占用 200 字节。所以,mytalents[0] 和 yourtalents[0] 都分别表示一个字符串,但 mytalents 和 yourtalents 的类型并不相同。mytalents 中的指针指向初始化时所用的字符串字面量的位置,这些字符串字面量被储存在静态内存中,而 yourtalents 中的数组则储存着字符串字面量的副本,所以每个字符串都被储存了两次。yourtalents 中的每个元素的大小必须相同,而且必须是能储存最长字符串的大小。

如果要用数组表示一系列待显示的字符串,请使用指针数组。因为它比二维字符数组的效率高;

四、字符串的输入输出

4.1、scanf()函数与printf()函数

  scanf() 函数的函数原型如下:

int scanf( const char * restrict format, ... );

  在 C 语言中,格式输入使用 scanf() 函数,scanf() 函数通过 % 转义的方式可以获取用户通过标准输入设备输入的数据 。用户从键盘输入的是文本数据,scanf() 函数把输入的字符串转换成指定的格式,并将转后成指定格式后的数据保存在指定变量中。

  通过 scanf() 函数的一般格式可以看出, 格式字符串 是用双引号括起来的字符串,其中包括 转换说明 (conversion specification)和 字面字符 两种 。而在地址列表中,此处应该给出用来接收数据变量的地址 。我们可以使用 %s 格式说明输入一个字符串。

  scanf() 函数有两种方法确定输入结束。无论使用哪种方法,都从第 1 个非空白字符作为字符串的开始,如果使用 %s 格式说明,以一下一个空白字符(空行、空格、制表符或换行符)作为字符串的结束(字符串不包括空白字符)。如果指定字符宽度,那么 scanf() 将读取指定的字符个数或读到第 1 个空白字符停止(先满足的条件即是结束输入的条件)。

  scanf() 函数会返回一个整数,该值等于 scanf() 成功读取的项数或 EOF(读到文件结尾时返回 EOF)。

  printf() 函数的函数原型如下:

int printf( const char * restrict format, ... );

  printf() 函数的作用是向终端(输出设备)输出若干任意类型的数据。printf() 语句把输出发送到一个叫做 缓冲区 (buffer)的中间存储区域,然后缓冲区的内容再不断发送到屏幕上。C 标准明确规定了何时把缓冲区中的发送到屏幕:当缓冲区满、遇到换行字符或组要输入的时候(从缓冲区把数据发送到屏幕或文件被称为 刷新缓冲区 )。

  通过 printf() 函数的一般格式可以看出,格式字符串 是用双引号括起来的字符串,其中包括 转换说明 (conversion specification)和 字面字符 两种。printf() 函数把字符串的地址作为参数。我们可以使用 %s 格式说明输入一个字符串。printf() 函数不会自动在每个字符串末尾添加一个换行符。因此,必须在参数中指明应该在哪里使用换行符。

#include <stdio.h>

int main(void)
{
    char name1[11], name2[11];
    int count;

    printf("Please enter 2 names.\n");
    count = scanf("%5s %10s", name1, name2);
    printf("I read the %d names %s and %s.\n", count, name1, name2);

    return 0;
}

由于 scanf() 函数无法知道要输入字符串的大小,必须遇到换行符或读到文件结尾位置才接收输入,因此容易导致字符数组越界的情况。不过,在 %s 格式说明中使用字段宽度可防止溢出;

4.2、gets()函数与puts()函数

  gets() 函数的函数原型如下:

char * gets(char * str);

 gets() 函数读取整行输入,直至遇到换行符,然后丢弃换行符,储存其余字符,并在这些字符的末尾添加一个空字符 '\0' 使其成为一个 C 字符串。

  gets() 函数无法检查实参数组是否能装下输入行。这是因为数组名会转换成该数组首元素的地址。因此,gets() 函数只知道数组的开始处,并不知道数组中有多少个元素。如果输入的字符串过长,会导致缓冲区溢出(buffer overflow),即多余的字符超出了指定的目标空间。如果这些多余的字符只是占用了尚未使用的内存,就不会立即出问题;如果它们擦写了程序中的其它数据,会导致程序异常终止,或者还有其它情况。

  puts() 函数的函数原型如下:

int puts(char * str);

  字符串输出就是将字符串输出到控制台上。在 C 语言中,字符串输入使用的是 puts() 函数,它的作用是输出字符串并显示在屏幕上。puts() 函数只需要把字符串的地址作为参数传递给它即可。puts() 函数在显示字符串时会自动在其末尾添加一个换行符,该函数在遇到空字符 '\0' 时就停止输出,所以必须保证有空字符。

#include <stdio.h>

#define SIZE 81

int main(void)
{
    char words[SIZE];

    puts("Enter a string, please.");
    gets(words);
    puts("Your string:");
    puts(words);
    puts("Done.");

    return 0;
}

C11 标准委员会采取了更强硬的态度,直接从标准中废除了 gets() 函数;

4.3、fgets()函数与fputs()函数

  fgets() 函数的函数原型如下:

char * fgets( char * restrict str, int count, FILE * restrict stream );

  过去通常用 fgets() 来代替 gets()。fgets() 函数的第 2 个参数限制读入的字符数来解决溢出问题。该函数专门设计用于处理文件输入。fgets() 和 gets() 的区别如下:

  • fgets() 函数的第 2 个参数指明读入字符的最大数量。如果该参数的值是 n,那么 fgets() 将读入 n-1 个字符,或者读到遇到的第一个换行符为止;
  • 如果 fgets() 读到一个换行符,会把它储存在字符串中。这点与 gets() 不同,gets() 会丢弃换行符;
  • fgets() 函数的第 3 个参数指明要读入的文件。如果读入从键盘输入的数据,则以 stdin(标准输入)作为参数,该标识符定义在 stdio.h 中。

  fgets() 函数返回指向 char 的指针。如果一切进行顺利,该函数返回的地址与传入的第 1 个参数相同。但是,如果函数读到文件结尾,它将返回一个特殊的指针:空指针(null pointer)。该指针保证不糊指向有效的数据。

  fputs() 函数的函数原型如下:

int fputs( const char *restrict str, FILE *restrict stream );

  因为 fgets() 函数把换行符放在字符串的末尾(假设输入行不溢出),通常要与 fputs() 函数配对使用,除非该函数不在字符串末尾添加换行符。fputs() 和 puts() 的区别如下:

  • fputs() 函数的第 2 个参数执行要写入数据的文件。如果要打印在显示器上,可以用定义在 stdio.h 中的 stdout(标准输出)作为该参数;
  • 与 puts() 函数不同,fputs() 函数不会在输出的末尾添加换行符。

  系统使用缓冲的 I/O。这意味着用户按下 Return 键之前,输入都被储存在临时储存区(即,缓冲区)中。按下 Return 键就在输入中增加了一个换行符,并把整行输入发送给 fgets()。对于输出,fputs() 把字符发送给另一个缓冲区,当发送换行符时,缓冲区的内容发送至屏幕上。

#include <stdio.h>

#define SIZE 14

int main(void)
{
    char words[SIZE];

    fputs("Enter a string, please.\n", stdout);
    fgets(words, SIZE, stdin);
    fputs("Your string:\n", stdout);
    fputs(words, stdout);
    fputs("Done.", stdout);

    return 0;
}

fgets() 获取字符串少于元素个数会有 '\n',大于等于没有 '\n'。

4.4、gets_s()函数

  gets_s() 函数的函数原型如下:

char * gets_s( char * str, rsize_t n );

  C11 标准新增的 gets_s() 函数也可代替 gets() 函数。该函数与 gets() 函数更接近,而且可以替换现有代码中的 gets() 函数。但是,它是 stdio.h 输入/输出函数系列中的可选拓展,所以支持 C11 的编译器也不一定支持它。gets_s() 函数与 fgets() 函数的区别:

  • gets_s() 函数只从标准输入中读取数据,所以不需要第 3 个参数;
  • 如果 gets_s() 读到换行符,会丢弃它而不是储存它;
  • 如果gets_s() 读到最大字符数都没有读到换行符,会执行以下几步。首先把目标数组中的首字符设置为空字符,读取并丢弃然后的输入直至读到换行或者文件结尾,返回返回空指针。接着,调用依赖实现的“处理函数”(或你选择的其它函数),可能会中止或退出程序;

  如果目标存储区装得下输入行,gets()、fgets() 和 gets_s() 3 个函数都没有问题。但是,fgets() 会保留输入末尾的换行符作为字符串的一部分,要编写额外的代码将其替换成空字符。

  如果输入行太长,使用 gets() 不安全,它会擦写现有数据看,存在安全隐患。gets_s() 函数很安全,但是如果并不希望程序中止或者退出,就要知道如何编写特殊的“处理函数”。另外,如果打算让程序继续运行,gets_s() 函数会丢弃输入行的其它字符,无论你是否需要。

五、字符串处理函数

  C库 提供了多个处理字符串的函数,ANSI C 把这些函数的原型放在 string.h 头文件中。其中最常用的函数有 strlen()、strcpy()、strncpy()、strcat()、 strncat()、strcmp()、strncmp() 等等。另外,还有 sprintf() 函数,其原型在 stdio.h 头文件中。

5.1、获取字符串长度函数

  strlen() 函数的函数原型如下:

size_t strlen( const char * str );    // 获取源字符串的有效长度

  strlen() 函数返回的是字符串中 '\0' 前面出现的字符个数(不包含 '\0')。str 参数指向的字符串必须要以 '\0' 结束。strlen() 函数的返回值是 size_t,是无符号的整数。

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

int main(void)
{
    char str[15] = "hello world!" ;
    int length = 0;

    length = strlen(str);

    printf("字符串的长度为:%d\n", length);

    return 0;
}

   模拟实现 strlen() 函数:

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

size_t my_strlen(const char * str);

int main(void)
{
    char str[] = "hello world!";
    int length = my_strlen(str);

    printf("%u\n", length);

    return 0;
}

size_t my_strlen(const char * str)
{
    size_t count = 0;

    assert(str);        // 断言,判断指针不能为空
  
    while(*str++ != '\0')
    {
        count++;
    }

    return count;
}

5.2、字符串复制函数

  strcpy() 函数 和 strncpy() 函数的原型如下:

char * strcpy( char * dest, const char * src );                   // 复制源字符串数组到目标字符串中
char * strncpy( char * dest, const char * src, size_t count );    // 复制源字串的n个字符复制到目标字符串中

  strcpy() 函数会将源字符串拷贝到目标字符串中,源字符串中必须以 '\0' 结束,它会将源字符串的 '\0' 拷贝到目标空间。并且,目标空间必须足够大,以确保能存放源字符串,目标空间还必须是可变的。

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

int main(void)
{
    char src[15] = "hello world!" ;
    char dest1[15] = {0};
    char dest2[15] = {0};
    char * str = "hello";	// 目标区域不可修改
  
    strcpy(dest1,src);
    printf("%s\n",dest1);

    strncpy(dest2,str,5);
    printf("%s\n",dest2);

    return 0;
}

  模拟实现 strcpy() 函数:

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

char * my_strcpy(char * desc, const char * src);

int main(void)
{
    char src[15] = "hello world!" ;
    char dest[15] = {0};
  
    my_strcpy(dest,src);
    printf("%s\n",dest);

    return 0;
}

char * my_strcpy(char * dest, const char * src)
{
    char * ret_val = dest;              // 记录目标空间原地址

    assert(src);                        // 断言,判断指针不能为空
    assert(dest);                       // 断言,判断指针不能为空

    while(*dest++ = *src++);

    return ret_val;                     // 返回目标空间原地址
}

5.3、字符串连接函数

  strcat() 函数 和 strncat() 函数的原型如下:

char * strcat( char * dest, const char * src );                 // 将源数组的字符串连接到目标数组后
char * strncat( char * dest, const char * src, size_t count );  // 将源数组的指定个数字符连接到目标数组后

  strcat() 函数可以将源字符串拼接到目标字符串后,源字符串必须以 '\0' 结束。并且,目标空间必须足够大,以确保能存放源字符串,目标空间还必须是可变的。

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

int main(void)
{
    char dest1[15] = "hello " ;
    char dest2[15] = "hello " ;
    char src[15] = "world!";
  
    strcat(dest1,src);
    printf("%s\n",dest1);

    strncat(dest2,src,5);
    printf("%s\n",dest2);

    return 0;
}

  模拟实现 strcat() 函数:

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

char * my_strcat(char * dest, const char * src);

int main(void)
{
    char dest[15] = "hello " ;
    char src[15] = "world!";
  
    my_strcat(dest,src);
    printf("%s\n",dest);

    return 0;
}

char * my_strcat(char * dest, const char * src)
{
    char * ret_val = dest;              // 记录目标空间原地址

    assert(src);                        // 断言,判断指针不能为空
    assert(dest);                       // 断言,判断指针不能为空
  
    while(*dest != '\0')
    {
        dest++;
    }

    while(*dest++ = *src++);

    return ret_val;                     // 返回目标空间原地址
}

5.4、字符串比较函数

  在 C 语言中,不能使用 == 来比较两个字符串,== 符号是比较两个字符串的首地址是否相等。

  字符串比较就是将一个字符串与另一个字符串从首字母开始,按照 ASCII 码的顺序进行逐个比较。在 C 语言中,通过 strcmp() 函数 或者 strncmp() 函数进行字符串的比较。

  strcmp() 函数 和 strncmp() 函数的原型如下:

int strcmp( const char * lhs, const char * rhs );                  // 将两个字符串从首字符按ASCII进行比较,相同返回0,lhs大返回整数,rhs大返回负数
int strncmp( const char * lhs, const char * rhs, size_t count );   // 将两个字符串前count字符按ASCII进行比较,相同返回0,lhs大返回整数,rhs大返回负数

  strcmp() 函数 和 strcmp() 函数的功能就是按照 ASCII 码的顺序比较两个数组中的字符串,并由函数返回值返回函数结果:

  • 字符串 1 = 字符串 2,返回值为 0
  • 字符串 1 > 字符串 2,返回值为 正数
  • 字符串 1 < 字符串 2,返回值为 负数
#include <stdio.h>
#include <string.h>

int main(void)
{
    char array1[15] = "hello world!" ;
    char array2[15] = "hello sakura!" ;
    int result = 0;

    result = strcmp(array1,array2);

    if(result < 0)
    {
        printf("第一个字符串大\n");
    }
    else if(result > 0){
        printf("第二个字符大\n");
    }
    else{
        printf("两个字符串相等\n");
    }

    result = strncmp(array1,array2,5);

    if(result < 0)
    {
        printf("第一个字符串大\n");
    }
    else if(result > 0){
        printf("第二个字符大\n");
    }
    else{
        printf("两个字符串前5个字符相等\n");
    }

    return 0;
}

  模拟实现 strcmp() 函数:

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

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

int main(void)
{
    char array1[15] = "abc" ;
    char array2[15] = "abc" ;
    int result = 0;

    result = my_strcmp(array1,array2);

    if(result < 0)
    {
        printf("第一个字符串大\n");
    }
    else if(result > 0){
        printf("第二个字符大\n");
    }
    else{
        printf("两个字符串相等\n");
    }

    return 0;
}

int my_strcmp(const char * str1, const char * str2)
{
    assert(str1);                           // 断言,判断指针不能为空
    assert(str2);                           // 断言,判断指针不能为空

    while(*str1 == *str2)
    {
        if(*str1 == '\0')
        {
            return 0;
        }
        str1++;
        str2++;
    }

    return *str1 - *str2;
}

与其说 strcmp() 函数按字母顺序进行比较,不如说按 机器排序序列(machine aollationg sequence)进行比较,即根据字符的数值进行比较(通常都使用 ANSCII 码);

5.5、查找子串函数

  strstr() 函数的原型如下:

char * strstr( const char * str, const char * substr );

  查找字符串 str 中是否包含子串 substr,如果找到则返回子串所在母串中的地址,找不到则返回 NULL。

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

int main(void)
{
    char str[] = "hello world!";
    char substr[] = "world";
    char * result = strstr(str, substr);

    result ? printf("%s\n", result):printf("子串不存在\n");

    return 0;
}

  模拟实现 strstr() 函数:

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

char * my_strstr(const char * str, const char * substr);

int main(void)
{
    char str[] = "abbbcdef";
    char substr[] = "bbc";
    char * result = my_strstr(str, substr);

    result ? printf("%s\n", result):printf("子串不存在\n");

    return 0;
}

char * my_strstr(const char * str, const char * substr)
{
    const char * s1 = str;
    const char * s2 = substr;
    const char * p = str;

    assert(str);                           // 断言,判断指针不能为空
    assert(substr);                        // 断言,判断指针不能为空

    while(*p)
    {
        s1 = p;
        s2 = substr;
        while(*s1 != '\0' && *s2 != '\0' && *s1 == *s2)
        {
            s1++;
            s2++;
        }

        if(*s2 == '\0')
        {
            return (char *)p;
        }

        p++;
    }

    return NULL;
}

5.6、切割字符串函数

  strtok() 函数的原型如下:

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

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

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

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

int main(void)
{
    char email[] = "sakura27185@petalmail.com";
    const char * delim = "@.";
    char * result = NULL;
    char temp[30];
  
    strcpy(temp, email);
  
    for(result = strtok(temp,delim); result != NULL; result = strtok(NULL, delim))
    {
        printf("%s\n", result);
    }

    return 0;
}

5.7、sscanf()函数与sprintf()函数

  sscanf() 函数的原型如下:

int sscanf( const char* buffer, const char* format, ... );

  sscanf() 函数声明在 stdio.h 头文件中。该函数与 scanf() 函数类似,但是它是从一个字符串中提取出格式化数据,并保留在对象的变量中。sscanf() 函数的第 1 个参数是字符串的地址。其余参数和 scanf() 函数相同,即格式说明符和待写入项的列表。

  sprintf() 函数的原型如下:

int sprintf( char *restrict buffer, const char *restrict format, ... );

  sprintf() 函数声明在 stdio.h 头文件中。该函数与 printf() 函数类似,但是它是把数据写入字符串,而不是打印在显示器上。因此,该函数可以把多个元素组合成一个字符串。sprintf() 函数的第 1 个参数是目标字符串的地址。其余参数和 printf() 函数相同,即格式字符串和待写入项的列表。

#include <stdio.h>

int main(void)
{
    char date[15] = {0};
    int year = 0,month = 0,day = 0;
  
    printf("请输入年月日,中间用-分割:\n");
    scanf("%d-%d-%d", &year, &month, &day);

    sprintf(date,"%4d-%02d-%02d", year, month, day);
    printf("字符串:%s\n", date);

    year = 0,month = 0,day = 0;
    printf("%04d-%02d-%02d\n", year, month, day);

    sscanf(date, "%d-%d-%d", &year,&month, &day);
    printf("%4d-%02d-%02d\n", year, month, day);

    return 0;
}

sprintf() 函数获取输入,并将其格式化为标准形式,然后把格式化的字符串储存在 formal 中;

posted @ 2023-03-07 12:03  星光樱梦  阅读(66)  评论(0编辑  收藏  举报