c语言中比较特殊的输入函数

目录

一.getchar()函数

1.基本功能

2.使用方法

(1).读取单个字符

(2).读取多个字符(直到遇到换行符)

(3).处理输入中的空白字符

3.返回值

4.应用场景

5.注意事项

二.fgets()函数

1.函数原型

2.工作原理

3.使用示例

(1).从标准输入读取一行字符串

(2).从文件中读取内容

4.处理换行符

5.与其他输入函数的比较

[(1). fgets vs gets](#(1). fgets vs gets)

[(2). fgets vs scanf](#(2). fgets vs scanf)

[(3). fgets vs fgetc](#(3). fgets vs fgetc)

6、常见错误和注意事项

[(1). 忘记检查返回值](#(1). 忘记检查返回值)

[(2). 未正确处理换行符](#(2). 未正确处理换行符)

[(3). 缓冲区大小不足](#(3). 缓冲区大小不足)

[(4). 多次读取时的缓冲区残留](#(4). 多次读取时的缓冲区残留)

7.综合示例

三.sscanf函数

1.函数原型

2.工作原理

(1).从字符串中解析数据

3.常见用法和注意事项

[(1). 解析特定格式的字符串](#(1). 解析特定格式的字符串)

[(2). 处理多余的输入](#(2). 处理多余的输入)

[(3). 不匹配的格式](#(3). 不匹配的格式)

[(4). 忽略特定数据](#(4). 忽略特定数据)

[(5). 返回值处理](#(5). 返回值处理)

4.常见错误和陷阱

[(1). 未匹配到预期的数据](#(1). 未匹配到预期的数据)

[(2). 忘记检查返回值](#(2). 忘记检查返回值)

[(3). 缓冲区溢出](#(3). 缓冲区溢出)

[(4). 处理多余输入](#(4). 处理多余输入)

[5.sscanf 与其他输入函数的比较](#5.sscanf 与其他输入函数的比较)

[(1). sscanf vs scanf](#(1). sscanf vs scanf)

[(2). sscanf vs fscanf](#(2). sscanf vs fscanf)

[(3). sscanf vs strtok](#(3). sscanf vs strtok)

6.综合示例

四.fscanf()函数

1.函数原型

2.工作原理

3.常用用法和注意事项

[(1). 读取一行数据](#(1). 读取一行数据)

[(2). 文件指针位置](#(2). 文件指针位置)

[(3). 遇到EOF](#(3). 遇到EOF)

[(4). 忽略特定数据](#(4). 忽略特定数据)

[(5). 返回值处理](#(5). 返回值处理)

[(6). 处理多余输入](#(6). 处理多余输入)

4.常见错误和陷阱

[(1). 格式不匹配](#(1). 格式不匹配)

[(2). 忘记检查返回值](#(2). 忘记检查返回值)

[(3). 文件结束符](#(3). 文件结束符)

[5.fscanf 与其他输入函数的比较](#5.fscanf 与其他输入函数的比较)

[(1). fscanf vs scanf](#(1). fscanf vs scanf)

[(2). fscanf vs sscanf](#(2). fscanf vs sscanf)

[(3). fscanf vs fgets](#(3). fscanf vs fgets)

6.综合示例


一.getchar()函数

getchar()是C语言中的一个标准库函数,用于从标准输入(通常是键盘)读取一个字符 。它是**stdio.h**库的一部分,常用于简单的字符输入操作。

1.基本功能

getchar()读取标准输入中的下一个字符,并将其作为一个**int类型的值返回**。尽管返回值是int类型,但它实际上是一个字符的ASCII值

2.使用方法

cpp 复制代码
int ch = getchar();

在这段代码中,getchar()从标准输入中读取一个字符,并将其返回的字符值存储在ch变量中。

示例:

(1).读取单个字符

cpp 复制代码
#include <stdio.h>

int main() {
    int ch;
    
    printf("请输入一个字符: ");
    ch = getchar();
    
    printf("你输入的字符是: %c\n", ch);
    
    return 0;
}

解释:

  • 程序等待用户输入一个字符。用户输入的字符被getchar()读取并存储在ch中。
  • printf函数随后输出用户输入的字符。

(2).读取多个字符(直到遇到换行符)

cpp 复制代码
#include <stdio.h>

int main() {
    int ch;
    
    printf("请输入一行文字,按回车结束: ");
    
    while ((ch = getchar()) != '\n' && ch != EOF) {
        printf("读取字符: %c\n", ch);
    }
    
    printf("输入结束。\n");
    
    return 0;
}

解释:

  • 程序使用while循环来读取字符,直到遇到换行符('\n')或文件结束符(EOF)。
  • 每次读取的字符都会被输出。

(3).处理输入中的空白字符

cpp 复制代码
#include <stdio.h>

int main() {
    int ch;

    printf("请输入字符,输入结束请按 Ctrl+D (Linux) 或 Ctrl+Z (Windows):\n");

    while ((ch = getchar()) != EOF) {
        if (ch == ' ' || ch == '\n' || ch == '\t') {
            printf("[空白字符]\n");
        } else {
            printf("读取字符: %c\n", ch);
        }
    }

    printf("输入结束。\n");
    
    return 0;
}

解释:

  • 这个程序读取用户输入的每个字符,并检查它是否是空白字符(空格、换行符、制表符)。
  • 如果是空白字符,程序会输出[空白字符],否则输出字符本身。
  • 当用户按下Ctrl+DCtrl+Z时,输入结束。

3.返回值

  • 读取的字符 : 如果读取成功,getchar()返回读取的字符的ASCII值。例如,如果用户输入字符'A'getchar()将返回值65(即'A'的ASCII码)。

  • EOF (End of File) : 如果在读取时遇到文件结束符(通常是在控制台输入时按下Ctrl+DCtrl+Z),getchar()将返回常量EOF,其值通常为-1EOF用于指示输入的结束或错误情况。

4.应用场景

  • 简单字符输入 : getchar()通常用于简单的字符输入操作,例如读取单个字符或控制台中的用户输入。

  • 处理空白字符 : 当你需要明确处理空白字符(例如空格、制表符或换行符)时,getchar()特别有用。相比于scanfgetchar()不会跳过空白字符,因此你可以逐字符地处理输入。

  • 控制输入流 : getchar()在处理输入流时非常灵活,可以用于跳过不需要的字符。例如,在多行输入或复杂输入格式的情况下,它可以帮助你控制和过滤输入内容。

5.注意事项

  • 返回类型为int : 尽管getchar()用于读取单个字符,但它的返回类型是int而不是char。这样设计是为了能够返回EOF来表示文件结束或错误。为了避免潜在的错误,通常应该使用int类型来存储getchar()的返回值。

  • 缓冲区问题 : 在控制台输入时,用户输入的内容通常会先进入缓冲区。当按下回车键时,缓冲区中的内容才会被getchar()逐个读取。因此,getchar()不会在每个字符输入时立即返回,而是等待用户按下回车键。

  • 错误处理 : 当使用getchar()时,程序应该考虑EOF的可能性,尤其是在文件输入或批处理输入的情况下。忽略这一点可能会导致程序未正确处理输入结束的情况。

二.fgets()函数

fgets 是C语言中用于从文件流 中读取一行字符串的标准库函数。它提供了一种安全、方便的方法来读取输入,避免了诸如缓冲区溢出等常见问题。fgets 通常用于读取用户输入、从文件中读取数据等场景。

1.函数原型

char *fgets(char *str, int n, FILE *stream);

  • str : 指向字符数组的指针,fgets 会将读取到的字符串存储在该数组中。
  • n : 要读取的最大字符数,包括结尾的空字符 \0。也就是说,fgets 最多读取 n-1 个字符。
  • stream : 输入流指针,指明从哪个流中读取数据。常见的流有标准输入流 stdin,文件流等。

返回值

  • 成功 : 返回指向读取字符串的指针,即参数 str
  • 失败或到达文件末尾 : 返回 NULL

2.工作原理

fgets 函数从指定的输入流 stream 中读取字符,并将其存储到**str 指向的字符数组**中,直到发生以下任一情况:

  1. 读取了 (n - 1) 个字符 : 为了保证字符串以 \0 结尾,fgets 最多读取 n - 1 个字符。
  2. 遇到换行符 \n : fgets 会读取换行符,并将其存储在 str 中。
  3. 到达文件末尾 EOF: 如果在读取过程中遇到文件末尾,读取操作结束。

读取完成后,fgets 会在读取的字符串末尾自动添加一个空字符 \0,以表示字符串的结束。

3.使用示例

(1).从标准输入读取一行字符串

cpp 复制代码
#include <stdio.h>

int main() {
    char buffer[100];

    printf("请输入一行文本:");
    if (fgets(buffer, sizeof(buffer), stdin) != NULL) {
        printf("您输入的是:%s", buffer);
    } else {
        printf("读取输入时发生错误。\n");
    }

    return 0;
}

解释:

  • fgets(buffer, sizeof(buffer), stdin) 从标准输入读取最多 99 个字符(保留1个字符给 \0),并存储在 buffer 中。
  • 如果读取成功,程序会输出用户输入的内容。

注意 : 由于 fgets 会将换行符 \n 一并读取并存储,如果不想要这个换行符,需要手动去除。

(2).从文件中读取内容

cpp 复制代码
#include <stdio.h>

int main() {
    FILE *fp = fopen("example.txt", "r");
    if (fp == NULL) {
        perror("无法打开文件");
        return 1;
    }

    char line[256];
    while (fgets(line, sizeof(line), fp) != NULL) {
        printf("%s", line);
    }

    fclose(fp);
    return 0;
}

解释:

  • 打开名为 example.txt 的文件,读取其中的内容并逐行打印到标准输出。
  • fgets 在每次循环中读取一行内容,直到文件结束。

4.处理换行符

正如前面提到的,fgets 会将输入中的换行符 \n 一并读取并存储在目标字符串中。如果在处理时不需要这个换行符,可以使用以下方法去除:

方法1:手动检查并替换

cpp 复制代码
size_t len = strlen(buffer);
if (len > 0 && buffer[len - 1] == '\n') {
    buffer[len - 1] = '\0';
}

解释:

  • 首先获取字符串的长度 len
  • 如果最后一个字符是 \n,将其替换为 \0

方法2:使用 strcspn 函数

cpp 复制代码
buffer[strcspn(buffer, "\n")] = '\0';

解释:

  • strcspn 函数返回 buffer 中第一个匹配 \n 的位置索引,然后将该位置的字符替换为 \0

5.与其他输入函数的比较

(1). fgets vs gets

  • gets : 从标准输入读取一行,直到遇到换行符或文件结束符。不安全,因为无法指定读取的最大长度,容易导致缓冲区溢出。

  • fgets : 可以指定最大读取长度,安全性更高。建议始终使用 fgets 代替 gets

(2). fgets vs scanf

  • scanf: 按照指定的格式从输入中读取数据,默认会忽略空白字符,读取字符串时会在遇到空白字符时停止。

  • fgets: 读取整行输入,包括空白字符和换行符,更适合读取包含空格的字符串

示例:

cpp 复制代码
char str1[100], str2[100];

// 使用 scanf
scanf("%s", str1);
// 输入:Hello World
// str1 的值为:"Hello"

// 使用 fgets
fgets(str2, sizeof(str2), stdin);
// 输入:Hello World
// str2 的值为:"Hello World\n"

(3). fgets vs fgetc

  • fgetc: 每次从指定流中读取一个字符,适合逐字符处理输入。

  • fgets: 一次读取一行或指定长度的字符串,效率更高。

6、常见错误和注意事项

(1). 忘记检查返回值

在使用 fgets 时,应该始终检查其返回值,以确保读取成功。

错误示例:

cpp 复制代码
fgets(buffer, sizeof(buffer), stdin);
// 未检查返回值,可能导致程序处理空指针

正确示例:

cpp 复制代码
if (fgets(buffer, sizeof(buffer), stdin) != NULL) {
    // 处理读取的数据
} else {
    // 错误处理
}

(2). 未正确处理换行符

如前所述,fgets 会保留输入中的换行符,如果不想要这个换行符,需要手动去除。

(3). 缓冲区大小不足

确保提供给 fgets 的缓冲区足够大,以容纳预期的输入数据和结尾的空字符。

错误示例:

cpp 复制代码
char buffer[10];
fgets(buffer, 100, stdin); // 错误:缓冲区只有10个字节,却试图读取100个字符

正确示例:

cpp 复制代码
char buffer[100];
fgets(buffer, sizeof(buffer), stdin); // 正确:缓冲区大小与读取长度匹配

(4). 多次读取时的缓冲区残留

在某些情况下,输入流中可能残留未读取的字符,需要在下一次读取前清空缓冲区。

cpp 复制代码
char buffer1[50], buffer2[50];

printf("输入第一行:");
fgets(buffer1, sizeof(buffer1), stdin);

printf("输入第二行:");
fgets(buffer2, sizeof(buffer2), stdin);

如果在第一次输入时输入的字符超过了 buffer1 的大小,剩余的字符会留在输入缓冲区中,影响第二次读取。为避免这种情况,可以在每次读取后清空缓冲区。

清空缓冲区的方法:

cpp 复制代码
int c;
while ((c = getchar()) != '\n' && c != EOF);

7.综合示例

示例:读取用户输入并处理

cpp 复制代码
#include <stdio.h>
#include <string.h>

int main() {
    char name[50];
    int age;
    char input[100];

    // 读取姓名
    printf("请输入您的姓名:");
    if (fgets(name, sizeof(name), stdin) != NULL) {
        // 去除换行符
        name[strcspn(name, "\n")] = '\0';
    } else {
        printf("读取姓名失败。\n");
        return 1;
    }

    // 读取年龄
    printf("请输入您的年龄:");
    if (fgets(input, sizeof(input), stdin) != NULL) {
        // 将字符串转换为整数
        age = atoi(input);
    } else {
        printf("读取年龄失败。\n");
        return 1;
    }

    printf("姓名:%s,年龄:%d\n", name, age);

    return 0;
}

解释:

  • 首先使用 fgets 读取用户的姓名,并去除结尾的换行符。
  • 然后再次使用 fgets 读取用户的年龄输入,并使用 atoi 将其转换为整数。
  • 最后输出用户输入的信息。

三.sscanf函数

sscanf 是C语言中用于从字符串中读取并解析数据的标准库函数。它的作用类似于 scanf,但 sscanf字符串中读取数据,而 scanf 是从标准输入中读取数据。sscanf 非常适合从已知格式的字符串中提取数值、字符等数据。

1.函数原型

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

  • str : 要解析的源字符串。sscanf 会从这个字符串中读取数据。
  • format : 格式字符串,指定了要解析的数据格式。它的语法和 scanf 的格式字符串相同,包括各种格式说明符(如 %d, %s, %f 等)。
  • ...: 可变参数列表,提供用于存储从字符串中读取的数据的指针。

返回值

  • 成功 : 返回成功读取并匹配的项数
  • 失败 : 返回 EOF,如果在解析过程中遇到错误或在尝试读取时到达字符串末尾

2.工作原理

sscanf 函数根据 format 字符串中的格式说明符,从源字符串 str 中逐一读取和解析数据。每个格式说明符都对应于一个传入的变量地址,sscanf 将解析后的数据存储在这些变量中。

格式说明符示例

  • %d: 读取一个整数。
  • %f: 读取一个浮点数。
  • %s: 读取一个字符串,遇到空白字符(如空格、换行)时停止。
  • %c: 读取单个字符。

示例:

(1).从字符串中解析数据

cpp 复制代码
#include <stdio.h>

int main() {
    const char *str = "100 3.14 hello";
    int num;
    float pi;
    char word[20];

    int count = sscanf(str, "%d %f %s", &num, &pi, word);

    printf("读取到 %d 项数据:\n", count);
    printf("整数: %d\n", num);
    printf("浮点数: %.2f\n", pi);
    printf("字符串: %s\n", word);

    return 0;
}

输出:

cpp 复制代码
读取到 3 项数据:
整数: 100
浮点数: 3.14
字符串: hello

解释:

  • sscanf 解析字符串 "100 3.14 hello",并将数据分别存储在 num, pi, word 中。
  • 返回值 count 表示成功读取了3项数据。

3.常见用法和注意事项

(1). 解析特定格式的字符串

sscanf 特别适用于解析具有固定格式的字符串,比如日期、时间、IP地址等。

示例:解析日期

cpp 复制代码
#include <stdio.h>

int main() {
    const char *date = "2024-08-17";
    int year, month, day;

    sscanf(date, "%d-%d-%d", &year, &month, &day);

    printf("年: %d, 月: %d, 日: %d\n", year, month, day);

    return 0;
}

输出:

cpp 复制代码
年: 2024, 月: 8, 日: 17

解释:

  • 解析字符串 "2024-08-17" 中的年份、月份和日期,并分别存储在 year, month, day 中。

(2). 处理多余的输入

如果源字符串中的内容多于格式说明符指定的内容,sscanf 只会处理指定的部分,忽略其余部分。

示例:部分解析字符串

cpp 复制代码
#include <stdio.h>

int main() {
    const char *str = "42 3.14 some extra text";
    int num;
    float pi;

    int count = sscanf(str, "%d %f", &num, &pi);

    printf("读取到 %d 项数据:\n", count);
    printf("整数: %d\n", num);
    printf("浮点数: %.2f\n", pi);

    return 0;
}

输出:

cpp 复制代码
读取到 2 项数据:
整数: 42
浮点数: 3.14

解释:

  • sscanf 只解析了前两个数据项(整数和浮点数),忽略了字符串末尾的额外内容。

(3). 不匹配的格式

如果字符串中的内容与格式说明符不匹配,sscanf 会停止解析,并返回已成功读取的项数。

示例:格式不匹配

cpp 复制代码
#include <stdio.h>

int main() {
    const char *str = "hello 3.14";
    int num;
    float pi;

    int count = sscanf(str, "%d %f", &num, &pi);

    printf("读取到 %d 项数据:\n", count);
    return 0;
}

输出:

cpp 复制代码
读取到 0 项数据:

(4). 忽略特定数据

sscanf 还可以使用 * 来忽略某些输入数据,不存储在任何变量中。

示例:忽略数据

cpp 复制代码
#include <stdio.h>

int main() {
    const char *str = "42 skip this 3.14";
    int num;
    float pi;

    sscanf(str, "%d %*s %f", &num, &pi);

    printf("整数: %d\n", num);
    printf("浮点数: %.2f\n", pi);

    return 0;
}

输出:

cpp 复制代码
整数: 42
浮点数: 3.14

解释:

  • sscanf 使用 %*s 忽略了 "skip this" 字符串部分,只提取了整数和浮点数。

(5). 返回值处理

sscanf 的返回值用于检查是否成功解析了预期的项数。在实际编程中,应该根据返回值来验证解析操作的成功与否。

示例:检查返回值

cpp 复制代码
#include <stdio.h>

int main() {
    const char *str = "100 3.14";
    int num;
    float pi;

    int result = sscanf(str, "%d %f", &num, &pi);

    if (result == 2) {
        printf("成功读取两个数值:%d 和 %.2f\n", num, pi);
    } else {
        printf("解析失败,已成功读取 %d 项数据。\n", result);
    }

    return 0;
}

输出:

cpp 复制代码
成功读取两个数值:100 和 3.14

解释:

  • 如果 sscanf 成功解析了两个值,程序输出成功消息。否则,输出失败消息。

4.常见错误和陷阱

(1). 未匹配到预期的数据

如果输入字符串的格式与格式说明符不匹配,sscanf 会停止解析,返回已经成功解析的项数。确保格式字符串与输入字符串的格式一致非常重要。

(2). 忘记检查返回值

忽略 sscanf 的返回值可能导致错误的程序行为。检查返回值是验证解析操作成功与否的关键步骤。

(3). 缓冲区溢出

当读取字符串数据(如使用 %s)时,确保目标缓冲区有足够的空间以避免溢出。使用长度限制格式符号(如 %99s)可以防止缓冲区溢出。

(4). 处理多余输入

如果 sscanf 只读取了部分输入数据,而程序的逻辑依赖于完整的数据解析,可能会导致问题。合理设计格式说明符和字符串输入非常重要。

5.sscanf 与其他输入函数的比较

(1). sscanf vs scanf

  • scanf: 从标准输入中读取数据,适用于直接的用户输入操作。

  • sscanf: 从字符串中读取数据,适用于解析已经存在的字符串内容。

(2). sscanf vs fscanf

  • fscanf: 从文件流中读取数据,适用于从文件中解析数据。

  • sscanf: 从字符串中读取数据,适用于解析内存中的字符串。

(3). sscanf vs strtok

  • strtok: 用于分割字符串,可以按指定的分隔符逐一提取子字符串。

  • sscanf: 直接解析字符串中的数据,并将其转换为相应的数据类型。

6.综合示例

示例:解析复杂字符串

cpp 复制代码
#include <stdio.h>

int main() {
    const char *str = "Name: John Doe, Age: 30, Score: 85.5";
    char name[50];
    int age;
    float score;

    int count = sscanf(str, "Name: %[^,], Age: %d, Score: %f", name, &age, &score);

    if (count == 3) {
        printf("姓名: %s\n", name);
        printf("年龄: %d\n", age);
        printf("分数: %.1f\n", score);
    } else {
        printf("解析字符串失败。\n");
    }

    return 0;
}

输出:

cpp 复制代码
姓名: John Doe
年龄: 30
分数: 85.5

解释:

  • 使用 sscanf 从格式化字符串中提取姓名、年龄和分数。%[^,] 格式说明符用于读取直到遇到逗号 , 的所有字符,这样可以处理包含空格的姓名。

四.fscanf()函数

fscanf 是C语言中用于从文件读取并解析数据 的标准库函数。它的作用类似于 scanf,但 fscanf 从文件流中读取数据 ,而 scanf从标准输入中读取数据fscanf 适合从文件中逐行或逐项读取并解析数据。

1.函数原型

int fscanf(FILE *stream, const char *format, ...);

参数说明

  • stream : 文件指针,指向要读取的文件流(通常是通过 fopen 打开的文件)。
  • format : 格式字符串,指定了要解析的数据格式。它的语法和 scanf 的格式字符串相同,包括各种格式说明符(如 %d, %s, %f 等)。
  • ...: 可变参数列表,提供用于存储从文件中读取的数据的指针。

返回值

  • 成功 : 返回成功读取并匹配的项数
  • 失败 : 如果遇到文件结束符 EOF,则返回 EOF。如果遇到错误,返回负数。

2.工作原理

fscanf 函数根据 format 字符串中的格式说明符,从文件流 stream 中逐一读取和解析数据。每个格式说明符都对应于一个传入的变量地址,fscanf 将解析后的数据存储在这些变量中。

格式说明符示例

  • %d: 读取一个整数。
  • %f: 读取一个浮点数。
  • %s: 读取一个字符串,遇到空白字符(如空格、换行)时停止。
  • %c: 读取单个字符。

示例:从文件中解析数据

假设有一个文本文件 data.txt,内容如下:

cpp 复制代码
42 3.14 hello
cpp 复制代码
#include <stdio.h>

int main() {
    FILE *file = fopen("data.txt", "r"); // 打开文件进行读取
    if (file == NULL) {
        perror("无法打开文件");
        return 1;
    }

    int num;
    float pi;
    char word[20];

    int count = fscanf(file, "%d %f %s", &num, &pi, word);

    printf("读取到 %d 项数据:\n", count);
    printf("整数: %d\n", num);
    printf("浮点数: %.2f\n", pi);
    printf("字符串: %s\n", word);

    fclose(file); // 关闭文件
    return 0;
}

输出:

cpp 复制代码
读取到 3 项数据:
整数: 42
浮点数: 3.14
字符串: hello

解释:

  • fscanf 从文件 data.txt 中解析数据,并将数据分别存储在 num, pi, word 中。
  • 返回值 count 表示成功读取了3项数据。

3.常用用法和注意事项

(1). 读取一行数据

fscanf 通常按格式说明符读取数据,直到匹配结束,或者遇到文件结束符 EOF。它不会自动处理行结束符,因此如果要按行读取数据,可以结合 fgetssscanf 使用。

示例:逐行读取数据

假设文件 data.txt 内容如下:

cpp 复制代码
42 3.14 hello
43 2.71 world
cpp 复制代码
#include <stdio.h>

int main() {
    FILE *file = fopen("data.txt", "r");
    if (file == NULL) {
        perror("无法打开文件");
        return 1;
    }

    int num;
    float pi;
    char word[20];

    while (fscanf(file, "%d %f %s", &num, &pi, word) != EOF) {
        printf("整数: %d, 浮点数: %.2f, 字符串: %s\n", num, pi, word);
    }

    fclose(file);
    return 0;
}

输出:

cpp 复制代码
整数: 42, 浮点数: 3.14, 字符串: hello
整数: 43, 浮点数: 2.71, 字符串: world

(2). 文件指针位置

fscanf 读取数据后,文件指针会移动到读取结束的位置。下一次调用 fscanf 会从当前文件指针位置继续读取。

(3). 遇到EOF

fscanf 遇到文件结束符 EOF 时,它会返回 EOF,通常是 -1。这可以用来判断是否已经读取到文件末尾。

(4). 忽略特定数据

类似于 sscanffscanf 也可以使用 * 来忽略某些输入数据,而不存储在任何变量中。

示例:忽略数据

cpp 复制代码
#include <stdio.h>

int main() {
    FILE *file = fopen("data.txt", "r");
    if (file == NULL) {
        perror("无法打开文件");
        return 1;
    }

    int num;
    float pi;

    while (fscanf(file, "%d %*s %f", &num, &pi) != EOF) {
        printf("整数: %d, 浮点数: %.2f\n", num, pi);
    }

    fclose(file);
    return 0;
}

解释:

  • fscanf 使用 %*s 忽略了字符串部分,只提取了整数和浮点数。

(5). 返回值处理

sscanf 类似,fscanf 的返回值表示成功读取并解析的数据项数。在实际编程中,应该根据返回值来验证解析操作的成功与否。

示例:检查返回值

cpp 复制代码
#include <stdio.h>

int main() {
    FILE *file = fopen("data.txt", "r");
    if (file == NULL) {
        perror("无法打开文件");
        return 1;
    }

    int num;
    float pi;

    while (fscanf(file, "%d %f", &num, &pi) == 2) { // 期望成功读取2项数据
        printf("整数: %d, 浮点数: %.2f\n", num, pi);
    }

    fclose(file);
    return 0;
}

解释:

  • 只有在成功解析了两个值时才输出结果。如果 fscanf 未能成功读取两个值,将停止读取。

(6). 处理多余输入

如果文件中的内容多于格式说明符指定的内容,fscanf 只会处理指定的部分,忽略其余部分。

4.常见错误和陷阱

(1). 格式不匹配

如果文件中的内容与格式说明符不匹配,fscanf 会停止解析,并返回已成功读取的项数。确保格式字符串与文件内容格式一致非常重要。

(2). 忘记检查返回值

忽略 fscanf 的返回值可能导致错误的程序行为。检查返回值是验证解析操作成功与否的关键步骤。

(3). 文件结束符

fscanf 在读取到文件结束符 EOF 时会返回 EOF,这与常规的返回值检查不同,处理文件结束符时需要特别注意。

5.fscanf 与其他输入函数的比较

(1). fscanf vs scanf

  • scanf: 从标准输入中读取数据,适用于直接的用户输入操作。

  • fscanf: 从文件流中读取数据,适用于从文件中解析数据。

(2). fscanf vs sscanf

  • sscanf: 从字符串中读取数据,适用于解析内存中的字符串。

  • fscanf: 从文件流中读取数据,适用于从文件中解析数据。

(3). fscanf vs fgets

  • fgets : 从文件中读取一整行字符串,适用于逐行读取文件内容,通常结合 sscanf 使用以进一步解析数据。

  • fscanf: 按照格式说明符从文件中读取数据,适用于逐项解析数据。

6.综合示例

示例:从文件中读取并解析数据

假设有一个文本文件 students.txt,内容如下:

cpp 复制代码
John 20 85.5
Alice 22 90.0
Bob 21 78.5
cpp 复制代码
#include <stdio.h>

int main() {
    FILE *file = fopen("students.txt", "r");
    if (file == NULL) {
        perror("无法打开文件");
        return 1;
    }

    char name[50];
    int age;
    float score;

    while (fscanf(file, "%s %d %f", name, &age, &score) == 3) {
        printf("姓名: %s, 年龄: %d, 分数: %.1f\n", name, age, score);
    }

    fclose(file);
    return 0;
}

输出:

cpp 复制代码
姓名: John, 年龄: 20, 分数: 85.5
姓名: Alice, 年龄: 22, 分数: 90.0
姓名: Bob, 年龄: 21, 分数: 78.5

解释:

  • fscanf 从文件 students.txt 中解析每一行数据,并输出到屏幕上。它通过格式说明符 %s %d %f 解析每行中的姓名、年龄和分数。

完!

一键三连支持一下吧,非常感谢!

相关推荐
菜鸟学Python6 分钟前
Python 数据分析核心库大全!
开发语言·python·数据挖掘·数据分析
一个小坑货13 分钟前
Cargo Rust 的包管理器
开发语言·后端·rust
bluebonnet2718 分钟前
【Rust练习】22.HashMap
开发语言·后端·rust
古月居GYH18 分钟前
在C++上实现反射用法
java·开发语言·c++
在下不上天44 分钟前
Flume日志采集系统的部署,实现flume负载均衡,flume故障恢复
大数据·开发语言·python
陌小呆^O^1 小时前
Cmakelist.txt之win-c-udp-client
c语言·开发语言·udp
I_Am_Me_1 小时前
【JavaEE进阶】 JavaScript
开发语言·javascript·ecmascript
重生之我是数学王子1 小时前
QT基础 编码问题 定时器 事件 绘图事件 keyPressEvent QT5.12.3环境 C++实现
开发语言·c++·qt
Ai 编码助手1 小时前
使用php和Xunsearch提升音乐网站的歌曲搜索效果
开发语言·php
学习前端的小z1 小时前
【前端】深入理解 JavaScript 逻辑运算符的优先级与短路求值机制
开发语言·前端·javascript