【问题标题】:How do escape sequences actually behave in the scanf() function转义序列在 scanf() 函数中的实际行为如何
【发布时间】:2020-07-15 12:25:48
【问题描述】:

我浏览了一些东西以找出答案,但没有找到合适的东西(或者我是一个糟糕的搜索者)。

现在我正在从 stdio.h 头文件中学习 scanf 函数,他们说这里不推荐使用转义序列,因为它会“混淆”输入,但实际上会发生什么?我测试了一些代码并得到了不同 ES 的结果:

#include <stdio.h>

int main(void)
{
    int a, b, c;

    printf("Enter values: \n");
    scanf("%d%d%d\n", &a, &b, &c);
    printf("The values are %d, %d and %d.\n", a, b, c);
}

Enter values: 
1
2
3
randomstuff
The values are 1, 2 and 3.

'\t' 序列也是如此——它要求再输入一个值(我输入了 'randomstuff'),它没有存储在任何地方。 另一方面,'\a' 和 '\b' 表现不同,不会影响输入:

    ...
    scanf("%d%d%d\a", &a, &b, &c);
    printf("The values are %d, %d and %d.\n", a, b, c);
}

Enter values: 
1
2
3
The values are 1, 2 and 3.

当我弄乱 ES 的位置时,它变得更加混乱。 '\n' 和 '\t' 停止影响输入:

    ...
    scanf("%d\t%d\n%d", &a, &b, &c);
    printf("The values are %d, %d and %d.\n", a, b, c);
}

Enter values: 
1
2
3
The values are 1, 2 and 3.

而 '\a' 和 '\b' 开始产生一些影响:

    ...
    scanf("%d\b%d%d", &a, &b, &c);
    printf("The values are %d, %d and %d.\n", a, b, c);
}

Enter values: 
1    
The values are 1, 0 and 32766.

有人说 ES 不被 scanf() 解释,只是被视为普通字符,但这似乎也不正确,因为它确实按照规则解释了像 \' 或 \" 这样的 ES。

那么发生了什么? PS,为什么最后的结果是0和32766?

【问题讨论】:

  • if (scanf("%d\b%d%d", &amp;a, &amp;b, &amp;c) != 3) exit(EXIT_FAILURE);
  • 转义序列只是字符,scanf() 像处理任何其他字符一样处理它们。
  • scanf's Format Specification Fields 表示“空白字符:空白 (' '); 制表符 ('\t'); 或换行符 ('\n')。空白字符导致 scanf 读取,但不存储输入中的所有连续空白字符,直到下一个非空白字符。格式中的一个空白字符匹配任何数字(包括 0)和组合输入中的空白字符数。"
  • ...但作为格式说明符 %d%f%s 自动过滤前导空格,不需要显式过滤它。 %c%[...](和 %n)没有。

标签: c scanf


【解决方案1】:

scanf 收到一个字符串时,其中没有转义序列。如果将字符串文字用作参数,则在翻译(编译)程序时会处理其中的转义序列。 C 6.4.4.4 指定如何处理转义序列:

  • \'\"\?\\ 分别变为 '"?\
  • \a\b\f\n\r\t\v 成为警报、退格、换页、换行、回车、水平制表符和垂直制表符的字符,分别。
  • \d\dd\ddd,其中每个 d 是一个八进制数字,成为具有该值的字符。
  • \x 后跟一个或多个十六进制数字将成为具有该值的字符。

那么对于scanf,字符有含义:

  • 如果字符是空白字符(空格、水平制表符、换行符、垂直制表符、换页符),它会指示 scanf 读取输入直到第一个非空白字符(未读取)或直到无法读取更多字符。
  • 如果字符是%(可能来自八进制或十六进制转义序列),它会引入转换规范,例如%d
  • 否则,它指示scanf 读取下一个字符并按字面意思进行匹配(格式字符串中的警告字符必须与输入流中的警告字符匹配,依此类推)。

'\t' 序列也是如此——它要求再输入一个值(我输入了 'randomstuff'),它没有存储在任何地方。

字符串文字中的\t 成为(水平)制表符,制表符是空白字符,因此它指示scanf 读取输入,直到它看到空白字符或无法获得更多输入.这就是为什么当你添加 \t 时,scanf 一直在阅读,直到它看到你的“randomstuff”中的非空白字符“r”。

另一方面,'\a' 和 '\b' 的行为不同并且不影响输入:

这些不是空白字符,也不是%,因此它们指示scanf 尝试将它们与输入流中的字符进行逐字匹配。对于"%d%d%d\a"scanf 匹配三个%d 规范,然后再读取一个字符以尝试将其与警报字符匹配。那失败了,所以scanf 停止并为三个成功的匹配返回 3。 (scanf 也将不匹配的字符“放回”到输入流中。)

当我弄乱 ES 的位置时,它变得更加混乱。 '\n' 和 '\t' 停止影响输入:

"%d\t%d\n%d" 中,制表符和换行符指示scanf 以匹配空白字符。但是,作为%d 转换规范的一部分,这些通常已经被scanf 跳过,因此它们在%d 之前无效。

而 '\a' 和 '\b' 开始产生一些影响:

"%d\b%d%d" 处理第一个十进制数字,然后退格指示scanf 匹配退格字符。由于输入中没有退格字符,匹配失败,scanf 停止。

请注意,这些行为均不涉及scanf 处理转义序列。 scanf 正在处理它接收到的字符,即制表符、警报和退格等字符。它看不到原始的转义序列。

【讨论】:

  • 虽然还不是很清楚......据我所知,当我在行尾按带有空格字符的“输入”时,而不是确认我的最后一个值它读取的最后一个字符是 \n 或 \t,不是吗?但是当 \n 或 \t 后面跟着另一个 %d (%f、%u 等等,可能与 %c 不同)时,输入确认优先于字符读取,因为正如你所说,跟随 % 使得 \n 成为跳过,是吗?这最后一点现在对我来说是最暗淡的......至于 \a (\b),现在它是有道理的,输入等待用户给出 \a 或 \b,即使没有办法做到这一点,对吗?
【解决方案2】:

正如我在上面评论的那样,scanf 格式字符串中的空白字符会从输入中删除一个或多个空白字符。但\b 不是空格字符,因此必须在输入中匹配。如果您对scanf 返回的值进行了基本检查,这里应该是3(转换的项目数),您会看到这些值并未全部读取。

#include <stdio.h>

int main(void)
{
    int a, b, c, r;
    r = scanf("%d\b%d%d", &a, &b, &c);
    printf("return val = %d\n", r);
    return 0;
}

节目环节:

7 8 9
return val = 1

这就是为什么你有奇怪的输出:值没有分配给bc

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-07-29
    • 1970-01-01
    • 2021-11-13
    • 1970-01-01
    • 1970-01-01
    • 2016-04-26
    • 1970-01-01
    相关资源
    最近更新 更多