【第四章·键盘输入和屏幕输出】第三节:数据的格式化键盘输入-函数 scanf 中的格式修饰符

时间:2024-10-22 08:48:44

        与 printf() 类似,在函数 scanf() 的 % 和格式符中间也可插入如表 4-5 所示的格式修饰符。

        在用函数 scanf() 输入数值型数据时,遇到以下几种情况都认为数据输入结束:

  • 遇空格符、回车符、制表符(Tab)
  • 达到输入域宽
  • 遇非法字符输入

        注意,如果函数 scanf() 的格式控制字符串中存在除格式说明符以外的其他字符,那么这些字符必须在输入数据时由用户从键盘原样输入。【需严格按照 scanf 中的格式控制字符串进行键盘输入

综合案例演示

        【例 4.4】下面程序用于演示函数 scanf() 对输入数据的格式要求。

#include <stdio.h>

int main(void)
{
    int a, b;
    scanf("%d %d", &a, &b);
    printf("a = %d, b = %d\n", a, b);

    return 0;
}

(1) 当要求程序输出结果为 a = 12,b = 34 时,用户应该如何输入数据?

        答:因为程序第 6 行语句中的两个格式转换说明符之间是空格符,作为普通字符,它必须在用户输入时原样输入,即输入数据之间应以空格作为分隔符。所以此时应按以下格式输入数据:

12 34

(2) 当限定用户输入数据以逗号为分隔符,即输入数据限定为以下格式时,应修改程序中的哪条语句?怎样修改?

12,34

        答:如果要求输入数据之间以逗号分隔,那么两个格式转换说明符之间必须以逗号分隔,即应该将程序第 6 行语句修改为:

scanf("%d,%d", &a, &b);

(3) 当程序第 6 行语句修改为如下语句时,用户应该如何输入数据?

scanf("a = %d, b = %d", &a, &b);

        答:此时用户应在输入数据时将字符串 "a =" 和 "b =" 原样输入,即按以下格式输入数据:

a = 12, b = 34

(4) 当输入数据限定为以下格式,同时要求程序输出结果为 a = 12,b = 34 时,应修改程序中的哪条语句?怎样修改?

1234

        答:此时应将第 6 行语句修改为:

scanf("%2d%2d", &a, &b);

        这样在输入数据时,可以自动按照指定宽度从输入的数据中截取所需数据。 

(5) 当输入数据限定为以下格式,同时要求程序输出结果为 a = "12",b = "34" 时,应修改程序中的哪条语句?怎样修改?

12
34

        答:应修改程序中的第 7 行语句为:

printf("a = \"%d\", b = \"%d\"\n", a, b);

        这里,函数 printf() 格式控制字符串中的字符 \" 是转义字符,代表双引号字符。 

(6) 若使用户可以用任意字符作为分隔符输入数据,则程序应该如何修改?

        答:此时可使用忽略输入修饰符来实现用户以任意字符作为分隔符进行数据的输入,即将程序中的第 6 行语句修改为:

scanf("%d%*c%d", &a, &b);

        这是因为忽略输入修饰符使得对应的输入项在读入后不赋给任何变量,因此无论用户输入什么都不会对其对应的输入项(即分隔符)产生影响,也就意味着用户可以用任意字符作为分隔符来输入数据。此时,无论用户按以下哪一种数据输入格式输入数据,屏幕上都会显示 a = 12,b = 34 的结果。

  • %c:这原本是一个格式说明符,用于读取一个字符并将其存储在指定的字符变量中
  • *当 * 字符紧跟在格式说明符前面时(如 %*d、%*c 等),它指示 scanf 读取输入中相应的数据但不对其进行赋值。也就是说,数据被读取(并因此从输入缓冲区中移除),但不会存储在任何变量中。
  • 因此,%*c 的作用是读取(并忽略)输入流中的下一个字符。这个字符可以是任何字符,包括数字、字母、空格、制表符或换行符等。

        格式 1:以回车符作为数据分隔符

        格式 2:以空格符作为数据分隔符

        格式 3:以逗号作为数据分隔符

        格式 4:以制表符作为数据分隔符

        格式 5:以字符 - 作为数据分隔符

(7) 当第 6 行语句修改为如下语句时,如果用户输入 123456,那么程序运行结果如何?

scanf("%2d%*2d%2d", &a, &b);

        答:此时程序的输出结果为:

a = 12, b = 56

        其中,格式说明符 %*2d 中的 * 为忽略输入修饰符,表示对应的输入项(这里为 34)在读入后不赋给相应的变量,%2d 中的 2 为域宽附加格式说明,表示从输入数据中按指定宽度 2 从输入缓冲区中截取输入数据

(8) 如果用户输入了非法字符,如输入了 12 3a,那么程序运行结果如何?

        答:此时,程序运行结果为:

a = 12, b = 3

        这是因为当程序从输入数据中读取第 2 个数据时遇到了非法输入字符 a,于是第 2 个被读入的数据就是 3。

(9) 如果用户输入的是 123a,那么结果又会如何?

        答:此时,程序运行结果为:

a = 123, b = -858993460(一个随机的垃圾值,每次运行可能都不一样)

        这里由于用户不小心输入了一个非法字符而导致程序输入终止,使得函数 scanf() 未能读入指定的数据项数

        那么,如何判断函数 scanf() 是否成功读入了指定的数据项数呢?可以通过检查 scanf() 的函数返回值来实现

  • 当 scanf() 返回指定的数据项数时,表示函数被成功调用
  • 当 scanf() 返回 EOF 值(EOF 是在 stdio.h 中被定义为 -1 的宏常量)时,表示函数调用失败,即未能读入指定的数据项数。这需要使用第 5 章介绍的 if-else 语句才能编程实现。

(10) 如果程序第 6 行语句修改为下面语句,那么运行程序会出现什么结果?

scanf("%d %d", a, b); // 没有用取地址符 &

        答:此时运行程序后,程序会弹出一个对话框,使程序异常终止。

        本来应该在 scanf() 的参数地址表中用 &a 和 &b 指出接收数据的存储单元的地址,但是这里却变成了 a 和 b,这样就使得编译器误将 a 值和 b 值当做了地址值,使得数据试图存入这两个地址单元,从而导致了非法内存访问(即代码访问了不该访问的内存地址),而真正的地址为 &a 和 &b 的内存单元却未被存入数据,即变量 a 和 b 未被赋值。

        使用函数 scanf() 时忘记在变量前面加上取地址运算符,以指定用来接收数据的变量的地址,这是一个初学者常犯的错误。