转自:http://www.cnblogs.com/xiaocai905767378/archive/2011/06/01/2067526.html
就从函数scanf说起吧。对于学习C的朋友来说,最熟悉的函数除了入门第一个"Hello World"程序用到的printf外,恐怕非scanf莫属了吧?但思考一下,你真掌握scanf了吗?呵呵~或许scanf比你想象中要强大的多!下面来整理一下我实践中使用scanf时遇到过的问题以及经过翻书、百度、google来的资料:
一、关于字符输入的问题scanf("%c",&char)与scanf("%s",&char)
1.scanf("%c",&char)
先看一段程序:
#include <stdio.h>
#include <stdlib.h>
void ctoi();
void itoc();
int main(){
int choice;
printf("请选择:\n");
printf("1.从键盘中输入一个英文字母,打印输出该英文字母的ASCII码值:\n2.从键盘中输入一个ASCII码,屏幕显示对应的字符\n");
scanf("%d",&choice);
switch(choice){
case 1:ctoi();break;
case 2:itoc();break;
default: printf("Input Error!\n");
}
}
/*以下函数作用是从键盘中输入一个英文字母,打印输出该英文字母的ASCII码值*/
void ctoi(){
printf("请输入英文字母:");
char c;
scanf("%c",&c);//留意这一行
if((c>='A'&&c<='Z')||(c>='a'&&c<='z')){
int i;
i=c;
printf("%c的ASCII码为%d\n",c,i);
}
}
/*从键盘中输入一个ASCII码,屏幕显示对应的字符*/
void itoc(){
int i;
printf("请输入ASCII码(0-255):\n");
scanf("%d",&i);
if(i>=0&&i<=255){
char c;
c=i;
printf("ASCII码%i对于的字符为%c\n",i,c);
}else{
printf("输入的不是ASCII码,请重新输入!\n");
}
}
运行结果:
2.scanf("%2c",&char)
#include <stdio.h>
#include <stdlib.h>
void ctoi();
void itoc();
int main(){
int choice;
printf("请选择:\n");
printf("1.从键盘中输入一个英文字母,打印输出该英文字母的ASCII码值:\n2.从键盘中输入一个ASCII码,屏幕显示对应的字符\n");
scanf("%d",&choice);
switch(choice){
case 1:ctoi();break;
case 2:itoc();break;
default: printf("Input Error!\n");
}
}
/*以下函数作用是从键盘中输入一个英文字母,打印输出该英文字母的ASCII码值*/
void ctoi(){
printf("请输入英文字母:");
char c;
scanf("%2c",&c);//留意这一行
if((c>='A'&&c<='Z')||(c>='a'&&c<='z')){
int i;
i=c;
printf("%c的ASCII码为%d\n",c,i);
}
}
/*从键盘中输入一个ASCII码,屏幕显示对应的字符*/
void itoc(){
int i;
printf("请输入ASCII码(0-255):\n");
scanf("%d",&i);
if(i>=0&&i<=255){
char c;
c=i;
printf("ASCII码%i对于的字符为%c\n",i,c);
}else{
printf("输入的不是ASCII码,请重新输入!\n");
}
}
2.scanf("%s",&char)
#include <stdio.h>
#include <stdlib.h>
void ctoi();
void itoc();
int main(){
int choice;
printf("请选择:\n");
printf("1.从键盘中输入一个英文字母,打印输出该英文字母的ASCII码值:\n2.从键盘中输入一个ASCII码,屏幕显示对应的字符\n");
scanf("%d",&choice);
switch(choice){
case 1:ctoi();break;
case 2:itoc();break;
default: printf("Input Error!\n");
}
}
/*以下函数作用是从键盘中输入一个英文字母,打印输出该英文字母的ASCII码值*/
void ctoi(){
printf("请输入英文字母:");
char c;
scanf("%s",&c);//留意这一行
if((c>='A'&&c<='Z')||(c>='a'&&c<='z')){
int i;
i=c;
printf("%c的ASCII码为%d\n",c,i);
}
}
/*从键盘中输入一个ASCII码,屏幕显示对应的字符*/
void itoc(){
int i;
printf("请输入ASCII码(0-255):\n");
scanf("%d",&i);
if(i>=0&&i<=255){
char c;
c=i;
printf("ASCII码%i对于的字符为%c\n",i,c);
}else{
printf("输入的不是ASCII码,请重新输入!\n");
}
}
/*以上程序均在ubuntu8.04+gcc上运行通过*/
总结:使用scanf("%c",&c),程序不等我输入字符就自动结束了;而用scanf("%2c",&c),程序接受字符 输入,但没输出;用scanf("%s",&c),程序起到预期效果,有输入输出。按照表1scanf函数转换说明符的定义,要输入一个字符,我们很自然会想到用scanf("%c",&c)而非scanf("%s",&c)吧?但事实是后者才正确!到底其中有什么玄机呢???查了不少资料,结果尚未有答案!哪位高手要是知道的话麻烦赐教啦!
今天找到了答案:
*************************************************************************************
问题的关键是scanf函数的运行机制:
当连续进行数值与字符的输入时,一旦输入的不是数字或小数点,系统就将它解释为字符,赋给了后面的字符变量。
程序中你用scanf("%f",&num);(尽管有printf一名,但没有作用)后面跟着又scanf("%c",&people);时,系统将你的输入num后的回车(或空格)赋给了people。因此程序不能正确执行。
当你用%s输入时,系统是将空格或回车解释为前一项结束,后一项开始,因此可以正确执行程序。
要解决这个问题,你可将字符的输入换成:
people=getche();
lab=getche();
应能解决。
来自: ltopic.csdn.net/t/20020520/12/736575.html
*************************************************************************************
二、scanf函数的格式说明
摘自: www.neu.edu.cn/cxsj/online/C4/C4_4_2.html
格式说明字符串规定了输入项中的变量将以何种类型的数据格式(由转换说明符给出)被输入,格式控制字符串的一般形式:
% [修饰符] 转换说明符
其中修饰符为任选项。
1)格式转换说明符:用于指定相应输入项内容的输入格式,常用格式见下表1。
表1 scanf函数转换说明符
格式 |
意义 |
d |
输入一个十进制整数 |
o |
输入一个八进制整数 |
x |
输入一个十六进制整数 |
i |
输入一个有符号或无符号的十进制、八进制、十六进制整数 |
u |
输入一个无符号十进制整数 |
f 、e或E、 g或G |
输入一个小数形式或指数形式的浮点数 |
c |
输入一个字符 |
s |
输入一个字符串 |
例如:
int x;
scanf(“%d”,&x);
它有两个参数“%d”和&x,第一个参数是格式控制字符串,由%后接一个类型转换说明符构成,指出用户应该输入的数据类型,转换说明符%d说明输 入的数据应该是一个整数。第二个参数是变量x的地址,&与变量名连用是将变量x的内存地址告诉scanf函数,计算机然后就会将输入的数据存储在 这个地址单元中。
计算机在执行scanf语句时,等待用户输入变量x的值,用户通过键入一个整数并按下回车键响应请求,计算机把用户的输入值赋给变量x,操作完成后,对x 的引用就会使用这个值。scanf函数(及后面学习的printf函数)提高了用户与计算机之间的交互性。
在有多个输入项时,如果格式控制字符串中没有普通字符或转义字符作为读入数据之间的分隔符,则一般采用空格符、<Tab>符或回车键作为读入 数据的分隔符,当C语言的编译系统空格符、<Tab>符或回车键以及非法字符时,会自动认为数据输入结束。计算机等待所有的数据输入结束后的 最后一次<回车键>,将读入的数据分别付给对应的变量所指定的内存单元。如果数据的输入少于格式控制字符串中指定的转换说明符的个数,则计算 机将一直等待数据的输入,直到所有数据全部被键入为止。
例如:
int x,y;
scanf(“%d%d”,&x,&y);
读入数据的方式可以是:
1<空格>2<回车>
或者
1<回车>
2<回车>
或者
1 <Tab>2<回车>
采用“%d%d”形式的格式字时,不能使用其它的数据读入方式。例如:1,2<回车>,会使得只有1被送入x单元,而y单元不能够得到数据2。
但是,在输入多个带有字符型数据时,若以空格符作为分隔符,可能产生非预期的结果。此时,空格将被作为有效字符处理。
例如:
int a;
char ch;
scanf(“%d%c”,&a,&ch);
如果数据读入方式为:123<空格>a<回车>,本意是期望变量a的值为数值32,变量ch的值为字符a,但实际上用于分隔数据 的空格被作为有效字符数据读入并赋予给字符变量ch。为了避免这种情况,可以在格式控制字符串中加入空格作为分隔符。将上面例句改为:scanf(“%d%c”,&a,&ch);此处的%d后的空格,就可以跳过字符‘a’前所有的空格,从而保证非空格数据的正确录入。
2)修饰符
scanf函数的修饰符有:数据读入宽度(域宽)、*和长度。修饰符和意义见下表2。
表2 修饰符以及意义
标识符 |
意义 |
域宽 |
指定输入数据的宽度 |
* |
跳过相应数据不作处理 |
l或h |
读入长整型、双精度型或短整型数据 |
①域宽
可以用一个十进制数指定输入数据的数据宽度,系统自动按域宽截取输入数据。
例如:
int a;
scanf(“%3d”,&a);
表示按宽度3输入一个整数给变量a。如果读入数据为:123456<回车>,则变量a实际接收的值为123。
例如:
int a,b,c;
scanf(“%2d%3d%4d”,&a,&b,&c);
如果读入数据为:123456789<回车>,则变量a、b 、c 的值分别是12、345和6789。可以实现数据的自动截取。 ②字符*
*表示按指定格式读入数据但不赋予相应的变量,作用是跳过相应的读入数据。
例如:
int a,b,c;
scanf(“%d%*d%d”,&a,&b,&c);
执行该语句,若输入为1? 2? 3<回车>,结果为a=1,b=3,c未赋值,2被跳过。
例1:一个实际问题——处理一个日期数据。
假设日期读入的格式为: 12-2-2003或12/02/2003,该数据格式中的年、月、日三个数据需要保存,但是连接年、月、日数据的连接符需要被废弃。
当用户以12-02-2003形式键入日期数据时,该数据中的每一个数值(年、月、日)需要被读入对应的变量year、month、date内存单元中, 为了去掉不需要的将年、月、日数据分开的连接符,直接方法是将这些字符包含在scanf的格式控制串中。
例如将语句写成:scanf(“%d─%d─%d’,&date,&month,&year);这条语句可以去掉以 12-2-2003形式读入数据中的连字符,但是当用户输入如下格式的日期数据::12/2/2003或12:2:2003时,该语句语句不仅不能去掉不 需要的字符(/或:),还会造成数据错误(只能正确得到date数据)。如果在输入格式字符串中使用scanf函数提供的*,将语句写成:
scanf(“%d%*c%d%*c%d’,&date,&month,&year);就能够从输入数据中读取有效数据并废弃任何%*c所指定的数据(不将其赋给某个变量)。
程序清单如下:
#include<stdio.h>
main( )
{
int month, day,yaer;
printf("Enter a date in the form d-m-y:");
scanf("%d%*c%d %*c%d",&date,&month,,&year);
printf("date=%d month=%d year=%d\n",date ,month,year);
}
运行结果:
Enter a date in the from d-m-y:12/3/2003
day=12,month=3,year=2003
③l和h
用于说明输入的数据时长整型(l)或短整型(h)。l和h可以和转换说明符d、o、x一起使用,形式为%ld、%lo、%lx、%hd、%ho、%hx,此外l还可以与f或e一起(%lf或%le)表示输入double型数据。
例如:
long a;
short b;
scanf(“%10ld%hd”,&a,&b);
表示变量a的数据按宽度为10的长整型读入,而变量b的数据按短整型读入。
3)普通字符(非格式字符)
格式控制字符串中除了格式字与修饰符外,还可以包含普通字符,这些普通字符包括:可打印字符、空格和转义字符。
①可打印字符:对scanf函数,如果格式控制字符串中的说明符之间包含有其他字符,那么在输入数据时,必须在相应位置读入这些字符。
例如 :
int a,b;
scanf(“%d,%d”,&a,&b);
若数据输入:1<空格>2;则只有变量a的数据是正确的,变量b则会发生错误。这是因为格式控制字符串中存在可打印字符“,”,所以在读入数据时,必须以“,”作为输入数据的分隔符。
正确地读入数据方式应为:1,2<回车>
又如:scanf(“a=%d,b=%f,c=%c”,&a,&b,&c);当输入为:1,2,a时,虽然采用了“,”分隔数 据,但也会产生错误,因为在格式控制字符串中还有其他的可打印字符(如:“a=”,“b=”,“c=”等)。也就是说,这些字符作为输入数据的分隔符,在 scanf函数读入数据时自动去掉。因此正确地数据读入方式应为:a=1,b=2.1,c=a<回车>
②空格
格式控制字符串中的空格可以分隔数据,在多个数据输入过程中,如果没有普通字符做数据的分隔符,则在数值数据输入时,可以用空格作为读入数据的分隔符,但在字符数据输入时,空格则不能作为数据之间的分隔符,它将被作为有效数据处理。
③转义字符
在以%c格式的数据读入中,转义字符被作为有效字符处理。而在格式控制字符串中的转义字符具有输入转义字符所代表的控制代码或特殊字符的功能。
请分析下面程序代码:
main()
{
int a,b;
scanf("%d%d\n",&a,&b);
printf("a=%d,b=%d\n",a,b);
}
如果输入1 2,会发生什么现象?应该怎样读入数据,才能得到执行结果?
提示 |
尽量不要在scanf()函数的格式控制字符串中出现普通字符,特别是转义字符,它会增加读入数据的难度并可能造成不可预料的错误。 |
三、 scanf函数的几个使用技巧
摘自: dev.csdn.net/article/10/10606.shtm
"哈哈哈,各位高手菜鸟,今天, 我来主讲scanf函数的一些用法","什么什么,这小子是不是欺负我们不识字呀!","scanf谁不会!".....台下一大堆臭鸡蛋,烂番茄如下雨 般扔上台来,有人喊"这小子铁定欠扁","一定是想来骗稿费!"。。。。。。。。。。。(作者:冤枉呀!写这种冬冬那里有钱可赚,我已穷的快卖血了, 呜。。。。。。。。。。。")
--------------------------------------------------------------------------------
-------------------------------------------
今天主要谈三个问题:
一.scanf函数输入格式中的字符串.
scanf函数输入格式中也可以含有普通字符串, 但他的含义是这些字符必须在输入中出现
,例如:
int num;
Scanf("hello %d", & num);
他的含义是首先要求输入一个hello字符串,然后再输入一个十进制数. 注意在等待输入时
忽略hello与要输入的数之间
的空格,制表符,回车. 因此这两种输入都是正确的:
hello 1234
hello1234
二.scanf函数的返回值.
看到一个学弟写的程序:
#include <stdio.h>
main()
{
int num;
printf("please input the student's score: ");
scanf("%d",&num);
if((num<0)||(num>100))
{
printf("The score put isnt fine. please run and input again.");
}
else if(num>90)
{
printf("The grade is A.");
}
else if((num>80)&&(num<90))
{
printf(..................
.............
}
..............
}
这个程序是没错,不过如果有人要存心捣乱, 输入时不是输入数字,而是其他的什么字
符,那么congratulations,
这个程序崩溃掉了. (^@^)
如何防止出现这种情况,有人通过把数字先读入到数组中,再判断读取的是不是一个数
字........., 作法真的好繁.
如果知道scanf函数的返回值的话,这个问题就好办多了. scanf函数执行成功时的返回
值为成功读取的变量数,
如果第一个变量的读取既告失败则返回值为0.
哈哈哈,我们可以通过判断scanf函数执行的返回值, 可以制止用户不正确地输入,从
而控制程序的流程.
把上面的程序改改:
#include <stdio.h>
main()
{
int num,int result=0;
printf("please input the student's score: ");
while(result==0)
{
fflush(stdin); /* 清空输入缓冲区. */
if(result!=1)printf("Please input a digital score: ");
result=scanf("%d",&num);
}
............................
}
一切OK!
三.scanf函数中一个参数的应用.
在scanf函数中,我们可以使用 %c来读取一个字符,使用 %s 读取一个字符串. 但是读取字
符串时不忽略空格,读字符串时忽略开始的空格,
并且读到空格为止,因此我们只能读取一个单词,而不是整行字符串.因此一般使用fgets来读
取一个字符串.
其实scanf函数也可完成这样的功能,而且还更强大.
这里主要介绍一个参数,%[ ] ,这个参数的意义是读入一个字符集合. [ ]是个集合的标
志,因此%[ ]特指读入此集合所限定的那些字符, 比如 %[A-Z] 是输入大写字母,一旦遇到不在
此集合的字符便停止. 如果集合的第一个字符是" ^ ", 这说明读取不在" ^ " 后面集合的字
符,既遇到" ^ " 后面集合的字符便停止.注意此时读入的字符串是可以含有空格的.
Eg. 输入一个字符串, 这个字符串只含有小写字符.遇到第一个不是小写字符时停止.
scanf("%[a-z],str);
Eg. 想输入一个字符串, 遇到 "." 停止,可设计如下:
scanf("%[^.]", str);
使用这个参数,你可以完成许多强大的功能呦!
--------------------------------------------------------------------------------
----------------------------------------
各位父老乡亲们, 这就是本人在用scanf函数时的一点小心得..............
(台下想起如雷的吼声,"这么简单的东西,你当我们是白痴呀!","退门票,退门票!",顿时,作者
被一大堆飞来的臭鞋掩埋............)
作者的话: 这是我的第一篇技术文章, 肯定会有一些错误,欢迎大家的指点. 其实我更喜欢C++, 也许以后会写一些C++的文章,欢迎高手的指点.
如果能或得大家的支持的话, 我会继续写的. (^-^)
作者: hyqryq
再说几句: 通常来讲,scanf函数和他的一些参数并不是很常用,主要是因为:
1.许多系统的scanf函数都有漏洞. (典型的就是TC再输入浮点型时有时会出
错).
2.用法复杂,容易出错.
3.编译器作语法分析时会很困难,从而影响目标代码的质量和执行效率.