【问题标题】:How to read in file properly from user input in C?如何从 C 中的用户输入正确读取文件?
【发布时间】:2021-03-07 13:52:18
【问题描述】:

我正在尝试读取用户输入并打开用户输入的文件。我的程序似乎在 print 语句之后立即退出。任何帮助将不胜感激!

  printf("What is the name of the file? \n");
  scanf("%s", "fileName");
  FILE* inFile = NULL;
  inFile = fopen("fileName", "r");
  if(inFile == NULL) {
    printf("Could not open this file");
  }

【问题讨论】:

  • 从不在 scanf 中使用 "%s" 作为转换说明符。这样做和使用gets 一样糟糕。
  • 总是检查scanf的返回值。
  • 始终在您的错误消息中提供系统错误,并将错误消息写入 stderr。 if( inFile == NULL ) { perror(fileName); exit(EXIT_FAILURE);}
  • 根本不要使用scanf(),直到您彻底了解其中的陷阱并知道为什么不应该使用它。声明一个足够大的数组,例如char line[1024]; 并读入if (fgets (line, sizeof line, stdin) == NULL) { /* handle EOF */ } 行,现在只需使用line[strcspn (line, "\n")] = 0; 从行尾修剪'\n' -- 完成。
  • 请发minimal reproducible example,以便我们重现问题并帮助您调试。

标签: c file input file-io


【解决方案1】:

问题的根源(使用scanf,但这完全是一个不同的问题)是:

scanf("%s", "fileName");

应该是:

char fileName[128];
if( 1 == scanf("%127s", fileName)) ...

您不能写入字符串文字,因此将字符串文字的地址传递给 scanf 是一场等待发生的灾难。你真的应该使用 PATH_MAX 而不是硬编码的 128,但是你需要使用 sprintf 来构造格式字符串,这似乎有损于这个例子。为了完整性:

#include <limits.h>
...
char fileName[PATH_MAX];
char fmt[64];
snprintf(fmt, sizeof fmt, "%%%ds", PATH_MAX - 1);

if( 1 == scanf(fmt, fileName) ){
    FILE* inFile = fopen(fileName, "r");
    if( inFile == NULL ){
        perror(fileName); 
        exit(EXIT_FAILURE);
    }
    ...
}

但请注意,您几乎肯定不想使用scanf 从输入流中读取输入文件。最好将其作为命令行参数并在argv 中提供。

【讨论】:

    【解决方案2】:

    您可以使用fscanf() 函数。

    【讨论】:

      【解决方案3】:

      继续我的评论,在您彻底理解scanf() 的缺陷之前,建议您对所有用户和文件输入使用fgets(),以便每次读取都使用完整的输入行。这样,输入流中剩余的内容不取决于使用的转换说明符以及是否发生 matching-failure。如果您随后需要从fgets() 读取的行中提取信息,则可以使用sscanf(),无论sscanf() 是成功还是失败,它都不会影响输入流中未读的内容。

      所有面向行的输入函数,fgets() 和 POSIX getline() 读取并包含 '\n' 作为它们填充的缓冲区的一部分。您只需在调用fgets() 之后使用strcspn() 来确定@​​987654332@ 之前的字符数,然后使用nul- 覆盖'\n',从存储行的末尾修剪'\n'终止字符'\0'(简单的ASCII 0)。

      您以完全相同的方式处理用户输入或从文件中读取的行。例如,您可以提示用户输入文件名,读取输入,打开文件,然后读取文件中的所有行,如下所示:

      #include <stdio.h>
      #include <string.h>
      
      #define MAXC 1024       /* if you need a constant, #define on (or more) */
      
      int main (void) {
          
          char fname[MAXC], line[MAXC];                   /* storage for filename & line */
          FILE *fp = NULL;                                /* FILE pointer */
          
          fputs ("enter filename: ", stdout);                     /* prompt */
          
          if (!fgets (fname, MAXC, stdin) || *fname == '\n') {    /* check EOF or empty line */
              puts ("(user canceled input)");
              return 1;
          }
          fname[strcspn (fname, "\n")] = 0;                       /* trim \n from end */
          
          if (!(fp = fopen (fname, "r"))) {               /* validate file open succeeds */
              perror ("fopen-fname");                     /* fopen sets errno on failure */
              return 1;
          }
          
          putchar ('\n');                                 /* provide space before output */
          while (fgets (line, MAXC, fp)) {                /* read each line from file */
              line[strcspn (line, "\n")] = 0;             /* trim \n from end */
              puts (line);                                /* output line */
          }
          
          fclose (fp);                                    /* close file when done */
      }
      

      请注意,在接受任何类型的输入时,必须验证每一步。否则,如果验证失败,输入失败,如果盲目使用包含不确定值的变量,则会调用 未定义行为

      使用/输出示例

      $ ./bin/readfile
      enter filename: dat/fleas.txt
      
      My dog has fleas
      My cat has none
      Lucky cat
      

      fgets() 提供了一种最强大的输入处理方式。您也可以使用scanf()——只要您了解与其使用相关的每个陷阱并防范它。如果您还有其他问题,请告诉我。

      【讨论】:

        猜你喜欢
        • 2021-10-29
        • 1970-01-01
        • 1970-01-01
        • 2012-01-24
        • 1970-01-01
        • 2021-09-01
        • 1970-01-01
        • 2015-03-31
        • 2021-03-23
        相关资源
        最近更新 更多