scanf() 语句中的格式字符串“%*[^\n]” 指示什么?赋值抑制器 (*) 和取反扫描集 ([^) 如何协同工作?

What does the format string "%*[^\n]" in a scanf() statement instruct? How do assignment suppressor (*) and negated scanset ([^) work together?

我知道引入带有 [ 转换说明符的扫描集,它随后指示要匹配或不匹配的字符,并额外插入 ^ 符号。

为此,在 ISO/IEC 9899/1999 (C99) 中规定:

The characters between the brackets (the scanlist) compose the scanset, unless the character after the left bracket is a circumflex (^), in which case the scanset contains all characters that do not appear in the scanlist between the circumflex and the right bracket.

因此,表达式 [^\n] 意味着它正在扫描字符,直到在相应的流中找到 \n 个字符,此处为 scanf()stdin\n 不是取自 stdin 并且 scanf() 继续处理下一个格式字符串(如果有),否则它会跳到下一个 C 语句。

接下来是赋值抑制运算符*:

为此,在 ISO/IEC 9899/1999 (C99) 中规定:

Unless assignment suppression was indicated by a *, the result of the conversion is placed in the object pointed to by the first argument following the format argument that has not already received a conversion result.

f.e 情况下的含义。 scanf("%*100s",a);stdin 中取出 100 个字符的序列,除非找到尾随的白色字符 space 但如果 a 是正确的,则不会分配给 a -定义 char 101 个元素的数组 (char a[101];).


但是现在 scanf() 语句中的格式字符串 "%*[^\n]" 实现了什么? \n是否留在stdin

赋值抑制器 * 和否定扫描集 [^ 如何协同工作?

这是否意味着:

  1. 通过使用 * 所有与此格式字符串匹配的字符都取自 stdin,但确定没有分配?,并且
  2. \n 不是取自 stdin 但它用于确定相应格式字符串的扫描操作?

我知道 [^* 每个人单独做什么,但不知道一起做什么。问题是这两者混合在一起的结果是什么,并结合了 \n.

的否定扫描集

我知道 Stack Overflow 上有一个类似的问题,它只涵盖了对 %[^\n] 的理解,这里是:What does %[^\n] mean in a scanf() format string。但是那里的答案对我的问题没有帮助。

%[^\n] 读取到但不包括下一个 \n 字符。在简单的英语中,它读取一行文本。通常,该行将存储在 char * 字符串变量中。

char line[SIZE];
scanf("%[^\n]", line);

* 修饰符抑制该行为。该行在读取后被简单地丢弃,不需要变量。

scanf("%*[^\n]");

* 不会改变输入的处理方式。在任何一种情况下,从标准输入读取直到但不包括下一个 \n 的所有内容。假设没有 I/O 错误,可以保证下一次从 stdin 读取时会看到 \nEOF.

Which scanf() statement should I use if I want to read and thereafter discard every character in stdin including the \n character?

添加 %*c 以同时消耗 \n

scanf("%*[^\n]%*c");

为什么 %*c 而不是 \n?如果您使用 \n 它不会只消耗一个换行符,它会消耗任意数量的空格、制表符和换行符。 \n 匹配任意数量的空格。最好使用 %*c 正好消耗 1 个字符。

// Incorrect
scanf("%*[^\n]\n");

另请参阅:

  • How to skip a line when fscanning a text file?

Could I use fflush() instead?

不,不要。 fflush(stdin) is undefined.

Isn't the negated scanset of [\n] completely redundant because scanf() terminate the scan process of the according format string at first occurrence of a white space character by default?

对于%s,是的,它将在第一个空白字符处停止读取。 %s 只读一个字。 %[^\n],相比之下,读取整行。它不会停在空格或制表符处,只会停在换行符处。

更一般地说,只有方括号中列出的确切字符才相关。空格没有特殊行为。与 %s 不同,它不会跳过前导空格,也不会在遇到空格时提前停止处理。

Does \n remain in stdin?

是的,确实如此。

But what does now the format string "%*[^\n]" in a scanf()-statement achieve?

它从输入流中读取所有字符,直到到达换行符并丢弃它们,而不从输入流中删除换行符。

By using * all characters matching to this format string are taken from stdin, but are not assigned?

正确。

\n isn't taken from stdin but it is used to determine the scan-operation for the according format string?

没错。当达到 \n 时,大多数 scanf 使用 ungetc 将字符推回输入流。

I know what each of those [^ and * do alone, but not together.

* 放在 [^ 之前与 [^ 完全相同,除了它不会将输入读入参数而是将其丢弃。

如果之后要丢弃 \n,请使用此格式字符串:

"%*[^\n]%*c"

由于它似乎没有被覆盖,所以读取换行符之前的所有内容,然后是换行符的工作方式是:

scanf("%*[^\n]%*c");
  • %*[^\n] 读取并丢弃直到下一个字符为换行符
  • %*c 只读取并丢弃一个字符,上面的字符将是换行符

你也可以用 %c 读取换行符到一个变量,看看你是否真的成功获得换行符,但你也可以直接检查 EOF 或直接错误,而不必为此烦恼。

  • %[^\n] 告诉 scanf 读取所有内容直到换行符 ('\n') 并将其存储在相应的参数中。
  • %*[^\n] 告诉 scanf 读取所有内容直到换行符 ('\n') 并丢弃它而不是存储它。

示例:

  • Hi there\n输入scanf("%[^\n]", buffer);得到buffer内容Hi there和剩下的stdin内容\n
  • Hi there\n 输入 scanf("%*[^\n]"); 导致 Hi there 被扫描并从 stdin 和剩余的 stdin 内容 \n 中丢弃。

请注意,如果遇到的第一个字符是 \n 字符,%[^\n]%*[^\n] 都会失败。一旦失败,stdin 保持不变,scanf returns 导致格式字符串的其余部分被忽略。


如果您希望使用 scanf 清除一行 stdin 直到并包括换行符,请使用

scanf("%*[^\n]"); /* Read and discard everything until a newline character */
scanf("%*c");     /* Discard the newline character */