gpt4 book ai didi

c - 如何在C语言中读取/解析输入?常见问题

转载 作者:行者123 更新时间:2023-12-02 07:28:52 25 4
gpt4 key购买 nike

尝试读取/解析输入时,我的C程序出现问题。

救命?

这是一个常见问题解答条目。

StackOverflow有许多与读取C语言中的输入有关的问题,答案通常集中在特定用户的特定问题上,而没有真正描绘出整个画面。

这是一种尝试全面解决许多常见错误的尝试,因此可以通过将这些特殊问题标记为该问题的重复项来回答这些特定的问题:

  • 为什么最后一行打印两次?
  • 为什么我的scanf("%d", ...) / scanf("%c", ...)失败?
  • 为什么gets()崩溃?
  • ...

  • 答案被标记为社区Wiki。随时改进和(谨慎地)扩展。

    最佳答案

    初学者的C输入入门

  • 文本模式与二进制模式
  • 检查fopen()是否失败
  • 陷阱
  • 检查成功调用的任何函数
  • EOF,或“为什么最后一行打印两次”
  • 不要使用gets()
  • 请勿在stdin或任何其他可供阅读的流上使用fflush()
  • 不要将*scanf()用于可能格式错误的输入
  • *scanf()无法正常工作时
  • 读取,然后解析
  • 通过fgets()读取(部分)输入行
  • 解析内存中的行
  • 清理


  • 文本模式与二进制模式

    完全按照写入方式读取“二进制模式”流。但是,可能会(或可能不会)在流的末尾附加一些实现定义的空字符(“ \0”)。

    “文本模式”流可以进行许多转换,包括(但不限于):
  • 删除行尾之前的空格;
  • 将换行符('\n')更改为输出的其他内容(例如Windows上的"\r\n"),然后将其换回输入的'\n'
  • 添加,更改或删除既不打印字符(isprint(c)为true),水平制表符或换行符的字符。

  • 很明显,文本模式和二进制模式不会混合使用。在文本模式下打开文本文件,在二进制模式下打开二进制文件。

    检查 fopen()是否失败

    尝试打开文件可能由于各种原因而失败-缺少权限,或者未找到最常见的文件。在这种情况下, fopen()将返回 NULL指针。在尝试读取或写入文件之前,请始终检查 fopen是否返回 NULL指针。

    fopen失败时,通常会设置全局 errno变量以指示失败的原因。 (从技术上讲,这不是C语言的要求,但是POSIX和Windows都保证可以做到这一点。) errno是可以与 errno.h中的常量进行比较的代码号,但是在简单的程序中,通常您要做的只是将其转换为错误消息,然后使用 perror() strerror() 进行打印。错误消息还应包括您传递给 fopen的文件名;如果您不这样做,当文件名不是您想的那样时,您将感到非常困惑。
    #include <stdio.h>
    #include <string.h>
    #include <errno.h>

    int main(int argc, char **argv)
    {
    if (argc < 2) {
    fprintf(stderr, "usage: %s file\n", argv[0]);
    return 1;
    }

    FILE *fp = fopen(argv[1], "rb");
    if (!fp) {
    // alternatively, just `perror(argv[1])`
    fprintf(stderr, "cannot open %s: %s\n", argv[1], strerror(errno));
    return 1;
    }

    // read from fp here

    fclose(fp);
    return 0;
    }

    陷阱

    检查成功调用的任何函数

    这应该是显而易见的。但是,请务必检查您调用的任何函数的文档以获取其返回值和错误处理,并检查这些条件。

    这些错误很容易在您早日发现病情时出现,但如果不及时发现,则会导致很多麻烦。

    EOF,或“为什么最后一行打印两次”

    如果已达到EOF,则函数feof()返回true。对“达到” EOF实际上意味着什么的误解使许多初学者写了这样的东西:
    // BROKEN CODE
    while (!feof(fp)) {
    fgets(buffer, BUFFER_SIZE, fp);
    printf("%s", buffer);
    }

    这会使输入的最后一行打印两次,因为读取最后一行(直到最后一个换行符,即输入流中的最后一个字符)时,EOF设置为而不是

    仅当您尝试读取最后一个字符时才设置EOF!

    因此,上面的代码再次循环,fgets()无法读取另一行,设置了EOF,并且buffer的内容保持不变,然后再次打印。

    相反,请检查fgets是否直接失败:
    // GOOD CODE
    while (fgets(buffer, BUFFER_SIZE, fp)) {
    printf("%s", buffer);
    }

    不要使用gets()

    There is no way to use this function safely.因此,随着C11的出现,它已从语言中删除。

    ,请勿在stdin或任何其他可供阅读的流上使用fflush()

    许多人期望fflush(stdin)放弃尚未读取的用户输入。它不会那样做。在纯ISO C中,在输入流上调用fflush()具有undefined behaviour。它在POSIX和MSVC中确实具有明确定义的行为,但是没有一个使它丢弃尚未读取的用户输入。

    通常,清除未决输入的正确方法是读取并丢弃直到换行符(包括换行符)的字符,但不能超过:
    int c;
    do c = getchar(); while (c != EOF && c != '\n');

    请勿将*scanf()用于可能格式错误的输入

    许多教程都教您使用*scanf()读取任何类型的输入,因为它用途广泛。

    但是*scanf()的目的实际上是读取可在某种程度上依赖于预定义格式的批量数据。 (例如由另一个程序编写。)

    即使这样,*scanf()也会使看不见的人绊倒:
  • 使用一个可能以某种方式受到用户影响的格式字符串是一个巨大的安全漏洞。
  • 如果输入的格式与预期格式不符,*scanf()将立即停止解析,保留所有剩余的参数未初始化。
  • 它会告诉您它已成功完成了多少个分配-这就是为什么您应该检查其返回码(请参见上文)的原因-但不能确切地知道它在何处停止了对输入的解析,因此很难进行正常的错误恢复。
  • 跳过输入中的所有前导空格,除非不这样做([cn转换)。 (请参阅下一段。)
  • 在某些极端情况下,它的行为有些特殊。

  • *scanf()无法正常工作时

    *scanf()经常出现的问题是,输入流中存在用户未考虑的未读空格(' ''\n',...)。

    读取数字("%d"等)或字符串("%s")会在任何空白处停止。尽管大多数*scanf()转换说明符都跳过输入中的前导空格,但[cn却不这样做。因此,换行符仍然是第一个待处理的输入字符,使得%c%[都不匹配。

    您可以通过显式读取它来跳过输入中的换行符,例如通过fgetc(),或在*scanf()格式字符串中添加空格。 (格式字符串中的单个空格匹配输入中任意数量的空格。)

    阅读,然后解析

    我们只是建议不要使用*scanf(),除非您真正肯定地知道自己在做什么。那么,用什么代替呢?

    无需像*scanf()那样一口气读取和解析输入,而是将步骤分开。

    通过fgets()读取(部分)输入行

    fgets()有一个参数,用于将其输入限制为最多字节,从而避免缓冲区溢出。如果输入行确实完全适合您的缓冲区,则缓冲区中的最后一个字符将是换行符('\n')。如果不是全部都适合,那么您正在看的是部分阅读的行。

    解析内存中的行

    对于内存中解析尤其有用的是strtol()strtod()函数族,它们提供了与*scanf()转换说明符diuoxaefg相似的功能。

    但是它们还会告诉您确切的停止解析的位置,并且对目标类型太大的数字进行有意义的处理。

    除了这些,C还提供了wide range of string processing functions。由于您已将输入存储在内存中,并且始终准确地知道已对其进行了解析,因此您可以随意走回很多次,以尝试理解输入。

    如果所有其他方法都失败,则您可以使用整行来为用户打印一条有用的错误消息。

    清理

    确保您明确关闭(成功)打开的所有流。这将刷新所有尚未写入的缓冲区,并避免资源泄漏。
    fclose(fp);

    关于c - 如何在C语言中读取/解析输入?常见问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35178520/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com