【问题标题】:Reading multiple strings from one line. Detecting invalid input从一行中读取多个字符串。检测无效输入
【发布时间】:2026-01-16 03:55:02
【问题描述】:

我正在尝试编写一个程序,在单个输入行中读取由单个空格分隔的 2 个单词。

正常输入应如下所示:Apple Banana。 Apple 应该写入 FirstWord,Banana 应该写入 SecondWord

目前我正在使用scanf 并用它读取字符串。 那是我的代码:

int main (void) {
  char FirstWord[10];
  char SecondWord[10];

  while (true) {
    printf("Enter two words: ");
    scanf("%s", FirstWord);  

    if(!strcmp(FirstWord, "quit")) {
      printf("Exit.");
      return 0;
    }
    scanf("%s", SecondWord);
    printf("First word is %s \nSecond word is %s\n", FirstWord, SecondWord);
  }
}

一般来说,程序可以运行,但是存在一个检测用户无效输入的问题。

如何检测用户是否只输入了一个单词或空行?现在第二个scanf 等待第二个字符串,以防万一只输入了一个单词。

是否可以用scanf 处理这个问题,或者我应该用 fgets 得到 1 个字符串,然后将它分成两个字符串?

【问题讨论】:

  • 使用fgets()读取一行,然后用sscanf()strtok()解析该行
  • 如您所见,scanf() 将阻止等待输入,它会忽略 空格,而'\n' 是空格,因此您可以多次按 Enter你喜欢它,它作为scanf() 的输入被简单地忽略(除非你使用%c%[..] 转换说明符)。 fgets() 读取并将 '\n' 包含在缓冲区中,它完全填充了行(当然还有足够大的缓冲区..)

标签: c validation input scanf fgets


【解决方案1】:

当您希望用户输入两个单词时,您还希望能够处理用户输入一个单词的情况(可能认为他们将在下一行输入下一个单词,等等......)将scanf()"%s" 转换说明符一起使用的问题是它忽略了前导空格,而'\n' 字符是空格。因此,如果只输入了一个单词,您就不必提示输入第二个单词,因为scanf() 将阻止等待第二个输入。

无论何时接受用户输入,我们都鼓励您使用fgets() 填充缓冲区(字符数组),然后从缓冲区解析(分离)所需的值。为什么? fgets() 将消耗一整行输入(给定足够大小的缓冲区)。这确保了stdin 中不会有任何内容未读 --- 只是在等待下一次输入时咬你。

然后,您可以使用 sscanf() 解析缓冲区中的值,就像使用 scanf() 解析输入中的值一样。但是,您已将输入和转换解耦,因此如果 sscanf() 转换失败,它不会影响您下次尝试从 stdin 读取。

使用fgets() 还提供了一种结束输入的便捷方式。由于fgets() 读取用户按Enter 生成的'\n' 字符,因此您只需检查缓冲区中填充的第一个字符是否是'\n' 字符即可知道用户只是按了在空白行上输入。这可以用作一种非常方便的方式来确定用户是否已完成,而无需额外输入,例如strcmp(FirstWord, "quit") 等...

如果我了解您希望能够无缝处理来自用户的一两个输入并在用户只提供一个的情况下提示,您可以这样做:

#include <stdio.h>

#define WORDSZ 32               /* if you need a constant, #define one (or more) */
#define MAXC 1024

int main (void) {
  
  char FirstWord[WORDSZ];                           /* declare arrays */
  char SecondWord[WORDSZ];
  char buf[MAXC];                                   /* buffer to hold entire line */
  
  puts ("press [Enter] on empty line when done.");
  
  while (1) {   /* loop continually until both filled or user cancels */
    fputs ("\nEnter two words: ", stdout);          /* prompt both */
    if (fgets (buf, MAXC, stdin) == NULL) {         /* read into buf, validate */
      puts ("(user canceled input)");
      return 0;
    }
    else if (*buf == '\n')                          /* if [Enter] on empty line */
      break;
    
    /* validate conversion, check if only one word read */
    if (sscanf (buf, "%31s %31s", FirstWord, SecondWord) == 1) {
        while (1) { /* if so, loop until second word read */
            fputs ("Enter second word: ", stdout);  /* prompt 2nd word */
            if (!fgets (buf, MAXC, stdin)) {        /* read into buf, validate */
              puts ("(user canceled input)");
              return 0;
            }
            
            if (sscanf (buf, "%31s", SecondWord) == 1)  /* validate word entered */
              break;
        }
    }
    
    printf ("\nFirst word is %s \nSecond word is %s\n", FirstWord, SecondWord);
  }
}

(注意:在使用 @987654339 时,您必须始终为所有字符串转换提供比数组大小小 1 的 field-width 修饰符,以保护数组边界@ 系列函数。否则使用scanf() 并不比使用gets() 更安全,参见Why gets() is so dangerous it should never be used!)

由于用户通过生成手动EOF 来取消输入是完全有效的,因此您需要在每次使用fgets() 读取后通过检查返回是否为NULL 来检查手动EOF。用户按 Ctrl + d(在 Windows 上为 Ctrl + z)会生成手册 EOF

使用/输出示例

$ ./bin/firstsecondword
press [Enter] on empty line when done.

Enter two words: hello
Enter second word: world

First word is hello
Second word is world

Enter two words: hello world

First word is hello
Second word is world

Enter two words:

查看一下,如果您还有其他问题,请告诉我。

【讨论】: