iOS开发——c语言——scanf函数详细讲解
2014-09-25 19:53
459 查看
scanf的使用详解
1、简介
scanf函数是标准的格式化输入函数,也就是从标准输入设备(键盘) 读取输入的信息。在stdio.h中声明,因此要使用scanf函数,必须加入#import <stdio.h>。使用scanf时候时,需要传入变量地址作为参数,且scanf函数会等待标准输入设备(键盘)输入数据,并将输入的数据赋值给变量地址对应的变量。且返回成功赋值的数据项,如果遇到错误或遇到end of file,返回值为EOF。(scanf是从标准输入(stdin)缓冲区中读取输入的数据)
2、基本用法
(1)、语法格式:scanf(“格式说明字符串”,变量地址);(变量地址必须有效,并且与格式说明符的顺序一致)
如:
(2)、执行过程分析:
使用scanf函数时,传入的参数,第一个是格式说明字符串,其它是一一对应的变量地址参数。执行scanf函数的时候,会等待标准设备输入,并不会往后执行代码。输入完毕后,敲一下回车键,目的是告诉scanf函数我们已经输入完毕了,scanf函数会将输入的值赋一一对应的赋值给后面的地址对应的变量。
如: int age;
scanf(“%d”, &age);
执行到scanf函数的时候,会等待输入。第一个参数是格式说明字符串,%d,说明要求用户输入十进制形式的一个整数。这里注意第二个参数不是age变量,而是age变量的地址&age,(&是c语言中的地址运算符。可以获取变量地址)。当用户输入完一个整数后,敲一下回车键。scanf函数会把输入的数据传递给地址对应的变量。并返回赋值成功个数。这样就执行完毕scanf这函数,继续后面代码的执行。
(3)注意点:
1》输入数据时候,遇以下情况结束一个数据的输入
*有间隔符合的情况下,以输入的间隔符为结束一个数据的输入的标识
(有间隔符合输入数据的时候必须添加间隔符号,否则报错,
注意在有间隔符号的情况下,它们之间如果加入空格会造成的影响)
*无间隔符合的情况下,以tab、空格、回车为结束一个数据输入的标准
*达到数据的宽度
*输入非法数据
2》scanf函数接收输入数据时,遇以下情况结束一个变量的赋值:(不是结束该scanf函数)
*读到自定义的间隔符
*默认情况下,读取到空白符,如tab、空格、回车。
*达到制定数据的宽度
*读到到非法输入,函数直接返回(后面的其它格式符都不会)
3》scanf函数结束情况
1d845
*每一个格式符均有对应的数据,然后按回车后结束
4》读到非法数据的处理方式:
完成输出,按回车键盘后。scanf函数将输入的数据赋值给后面对应位置的变量的时候,如果一遇到不匹配的就会直接返回,不再给后面变量赋值。如果第一个就不匹配,整个scanf函数中的,变量都不会赋值,或因缓冲区问题,赋错误值。
5》开头空白符号无效(char类型除外)
当一次多个变量赋值的时候,开头就使用空格、tab、回车,无效,只有在输入数据后面使用它们才会作为间隔或结束符合使用(char除外,对应char它们都是有效输入数据)
3、格式说明字符串详解
(1)、一般结构:%[*][宽度][F/N][h/l]类型字符
由三部分组成
1》格式符(格式化说明符)
2》空白符
3》非空白符
(2)、三个部分的详解:
1》格式符(格式化说明符
<strong>%a 读入一个浮点值(仅C99有效) %A 同上 %c 读入一个字符 %d 读入十进制整数 %i 读入十进制,八进制,十六进制整数 %o 读入八进制整数 %x 读入十六进制整数 %X 同上 %c 读入一个字符 %s 读入一个字符串 %f 读入一个浮点数 %F 同上 %e 同上 %E 同上 %g 同上 %G 同上 %p 读入一个指针 %u 读入一个无符号十进制整数 %n 至此已读入值的等价字符数 %[] 扫描字符集合 %% 读%符号</strong>
修饰符
*:星号 ,用于空读一个数据。
百分号(%)与格式符之间的星号(*)表示读指定类型的数据但不保存
L/l 长度修饰符 输入"长"数据
h 长度修饰符 输入"短"数据
W 整型常数 指定输入数据所占宽度
hh,ll同上h,l但仅对C99有效
2》空白符
空白字符,会使scanf函数读操作中略去输入中一个或多个空白符,空白符可以是空格、tab、回车
3》非空白符
非空白字符,会使scanf函数读操作中剔除与这个非空白字符相同的字符。但在输入时必须输入这些字符。否则就会出错 。一般作为间隔符合使用。
(2)、使用例子:
1》常用比较难的格式符例子:
*%f:用于接收或输出一个小数
printf(“<%f>",12.3456789);
输出为:<12.345679>
总结:默认输出6位小数,且如果原数据有第七位小数,四舍五入给第六位
float a;
scanf(“%f",&a); // 接收一个小数
printf(“<%f>",a); //输出接收的数据
输入:3.1234567 (注意有7位小数)
输出:3.123457
*%e的使用:%e是以指数形式输出或读起一个小数
例如:1.256e2 就是1.256*10的2次方
<strong>printf("%e",1123.45678); 输出:1.123457e+03 // 默认输出格式 //总结:默认输出6位小数。如果有第七位小数,四舍五入给第六位 float a; scanf(“%e",&a); printf(“%e",a); //输入:123.456789 ,输出 1.234568e+02 </strong>
2》宽度修饰符号的常用例子:%m.nX——》x代表格式符
* %m.nf/e——》m控制数据的整个长度,n控制小数的位数, n与m之间采用“.”作为分隔符号
与printf函数一起使用:
m控制整个输出数据的长度,如果m大于输出的数据长度,在数据前面使用空格填充,如果m小于默认输出数据的长度,m失效,而n控制小数的长度。其它格式:%mf/e、%.nf/e、%m.nf/e printf(“%.5f",5.123456789); ——》n是5表示输出5位小数 输出:5.12346 printf(“%4f",5.123456789); ——》m长度为4,小于默认输出数据的整体长,无效,使用默认输出 输出:5.123457 (默认输出长度是,保证小数是6位,对于f而言) printf("<%9f>",5.123456789); —》m大于默认输出的长度,前面使用空格填充 输出:< 5.123457> printf(“<%9.4f>”,5.123456789);——》同时使用m和n,之间使用.间隔 输出:< 5.1235>
与scanf函数一起使用:(没有精度控制)
只能有%mf/e这种格式,不能有%m.nf/e 或 %.nf/e,里面的“.”符号无效。但能控制读取的位数,m如果小于输入数,直接截取m位读取进去,如果m大于输入数,按原数读 例子: float a; scanf(“%2f”,&a); // m是2,只读取前2位数, printf(“%f",a); 输入:123.4567 ——》m是2,只读取前2位数 输出:12.000000 scanf(“%.3f",&a); // 报严重警告”.”是个无效符号(Invalid conversion specifier ‘.’) scanf(“%3.3e",&a); 也同样警告
3》%m.nd的使用
printf(“<%.9d>”,1234567); //.n格式:n如果大于整数长度,默认使用0 // 输出:<001234567> printf(“<%.4d>”,1234567); //.n格式:如果n小于其长度,n失效 // 输出:<1234567> printf(“<%9d>”,1234567); // m格式,如果大于整数的长度,使用空格填充 // 输出:< 1234567> printf("<%4d>",1234567); //如果m小于其长度,m失效 // 输出:<1234567> printf(“<%9.9d>",1234567); // 都相同的情况下,使用.n的效果 // 输出:<001234567> printf(“<%9.8d>",1234567); // 先采用前面的9,再采用后面的.8格式 // 输出:< 01234567>
int a; scanf(“%3d",&a); //m小于输入数,只截取m长度的数 printf("<%d>",a); //输入:1234567 //输出:<123>
int b; scanf(“%8d",&b); //当m大于输入数,m失效 printf(“<%d>",b); //输入:123 //输出:<123> scanf(“%.4d",a); // 不可以,报严重警告:点”.”无效符号<strong style="font-family: Helvetica; letter-spacing: 0px;"> </strong>
*scanf中没有精度控制
(4)、注意点
1》不可使用%f接收double类型的数据 ,会报下面的警告
默认情况下,a、f、e 和 g 告诉 scanf() 为 float 分配数据。 如果将 L / l放在这些修饰符的前面,则 scanf() 为 double 分配数据。使用 L 就是告诉 scanf(),接收数据的变量是
long double 型变量。小写l,表示普通的double类型
注意大写L,也是不可以接收double,而是long double类型
正确写法
long double a;
scanf(“%Lf",&a); // L修饰表示long
printf(“<%Lf>”,a);// 输出也要使用L
double a;
scanf(“%lf",&a); // 使用小写l,表示普通doule
2》参数是变量的地址,使用&取地址符(数组除外,不用&)
int num = 0;
scanf("%d", num);
printf("%d", num);
printf("请输入数字\n");
上面错误,修改为:
<strong>int num = 0; printf("请输入数字\n"); scanf("%d", &num); printf("%d", num);</strong>
3》如果只是定义一个指针,没有初始化,也就是没有指向具体数组。不可以拿来直接和scanf使用,原因:只是定义和分配了指针本身变量,而没有分配具体存储内容的空间(也就是没有任何指向指向)
如:
char *name;
scanf(“%s”, name); 是错误的,需要先指向具体数组
4、扫描字符集合的使用:%[]
(1)、基本认识,只能使用在1》 基本了解:
扫描集(scanset),是scanf后来添加的一个新的特性。
扫描集定义一个字符集合,只有在扫描集范围内的字符,才会被scanf读取并赋值给对应的字符数组。一旦遇到非法数据,立刻返回
如:
scanf(“%[abc]”,str); ——》表示只由满足abc这三个字符才能输入str里面
2》常用特殊符号:
^,放在首位,表示除了后面定义的之外的字符
如:
scanf(“%[^abc]”, str); ——》表示除了abc之外的字符都可以赋值给str数组
-:表示一个范围,如:
scanf(“%[a-z]”,str);——》表示a到z范围的小写都可以赋值给str数组
(注意只是小写,扫描集合区分大小写.%[a-zA-Z]表示大小写都可以)
(2)、常用技巧
1》限制接收的数据
*只接收a-z和A-Z的字母,scanf(“%[a-zA-Z]”,str);
2》接收带空格的字符串——》scanf("%[^\n]",
name);
<strong> #include <stdio.h> int main(int argc, const char * argv[]) { char name[20]; printf("请输入字符串\n"); scanf("%[^\n]", name); printf("%s\n", name); return 0; }</strong>
请输入字符串
hello world!入)
hello world! (输出)
分析%[^\n]这个扫描集:
^表示非、除了的意思,^\n表示除了“\n”都可以被scanf读取并赋值给字符数组。
3》stdin的缓冲区清空——》scanf(“%*[^\n]%*c”);清除一段文字(后面详细说)
(3)、注意点
1》因为扫描集,赋值的时候会以字符串的形式赋值给对应的字符数组,可使用%s直接输出,结果。而不用像c语音的数组遍历那样(所有注意输入的长度长度,不要多于数组元素个数)
<strong>#include <stdio.h> #include <string.h> int main(int argc, const char * argv[]){ char str[10]; printf("请输字符串\n"); scanf("%[a-zA-Z]", str); printf("str1=%s\n", str); // 可这样直接输出 printf(“输入的字符长度:%zd\n",strlen(str)); for(int i = 0; i<strlen(str);i++){ printf("str[%d]=%c\n", i, str[i]); } return 0; } /* 输入:aaaaaaaaaaaaaaaa 输出:str1=aaaaaaaaaaaaaaaa 输入的字符长度:16 str[0]=a,str[1]=a,str[2]=a,str[3]=a,str[4]=a, str[5]=a, str[6]=a,str[7]=a,str[8]=a,str[9]=a, str[10]=a,str[11]=a,str[12]=a,str[13]=a,str[14]=a,str[15]=a, */</strong>
2》%[]对间隔符的问题
<strong>//空白符,也会当作内容读取(类似给char赋值),如果字符集中没有空白符号,就会不匹配而返回 #include <stdio.h> int main(int argc, const char * argv[]){ char str1[10], str2[10]; printf("请输字符串\n"); scanf("%[a-zA-Z]%[a-zA-Z]", str1, str2); // 这里没有任何分隔符,所以str2永远也不会符合值的 printf("str1=%s\n", str1); printf("str2=%s", str2); return 0; } /* 输入:aaa□□bbbb 输出:str1=aaa str2= 分析: 扫描集合的使用中,会和单个字符赋值的时候,对空格符合的处理是一样的,就是会把它们也当作赋值内容的一分,而不是类似%d等类型符那样”忽略它们“。在读到a后面的一个□,会在str1的扫描集内比对,结果不满足其范围,这样scanf就相当于读取到非法数据,直接返回。这样就不会给str2赋值。 也有同学可能认为,如果分开写,这样“空白格符”会作为默认的间隔符使用吗?回答依然把“空白符”作为可赋值内容的一部分使用(只要存在了stdin的缓存问题) scanf("%[a-zA-Z]%[a-zA-Z]", str1, str2); (分开写成下面方式,也是无效的) scanf("%[a-zA-Z]", str1);scanf("%[a-zA-Z]", str2); */</strong>
<strong>// 如果定义的分隔符,在扫描集内,就会使间隔符失效。赋值发生错误 #include <stdio.h> int main(int argc, const char * argv[]) { char str1[10], str2[10]; printf("请输字符串\n"); scanf("%[a-zA-Z,],%[a-zA-Z,]", str1, str2); // 这里两个扫描集,以“,”分隔 printf("str1=%s\n", str1); printf("str2=%s", str2); return 0; } /* 输入:aaa,bbb 输出:str1=aaa,bbb str2= 结果分析,scanf对str1赋值操作中,把“,”当作自己的普通字符,赋给str1。这样stdin中的”,“就被scanf读取出来。而后面找不到与“,”这个分隔符匹配的字符,会造成匹配不到“,”这个字符的错误,scanf会直接返回。 , // 如果分隔符,不在扫描集内,输入的时候必须写分隔符 #include <stdio.h> int main(int argc, const char * argv[]){ char str1[10], str2[10]; printf(“请输字符串,使用#作为分隔符合\n”); scanf("%[a-zA-Z]#%[a-zA-Z]", str1, str2); // 采用#作为分隔符合 printf("str1=%s\n", str1); printf("str2=%s", str2); return 0; } /* 输入:aaabbb#cccddd (使用#分隔) 输出:str1=aaabbb str2=cccddd 分析: 使用#作为分隔符合,就必须要遵守。如果输入别的输入符合,或不满足的字符,scanf会自动返回,不会再继续赋值: 输入:aabb*cc#dd (scanf只要读取到不满足格式的字符,会立刻返回,而不会继续赋值) 输出:str1=aabb str2= 分开写 scanf("%[a-zA-Z]#%[a-zA-Z]", str1, str2);也会有上面效果 scanf("%[a-zA-Z]#", str1); scanf("%[a-zA-Z]", str2) (注意分汉字和英文情况输入) */ </strong>
5、一次给多个变量赋值:
(1)、简介:格式说明符可以由多个格式符组成,每个格式符都对应一个相同类型变量地址。scanf函数可以把输入的数据,根据格式符,依次赋值给相同类型的变量。默认使用tab、空格、回车作为间隔,也可以自己定义间隔符合如“,”、“-”、“#”等。但是要注意最后还是以回车键作为输入结束
(2)、常用例子:
1》使用scanf函数接收3个数
*默认中间不使用任何间隔符合
<strong>int a= 1 ,b= 1, c =1; scanf("%d%d%d",&a,&b,&c); printf("%d,%d,%d",a,b,c);</strong>
/*cccc 1、字符串的创建 (一般对象方法不用都是使用类方法) 直接赋值方式 (常用) alloc 、initxxx 偶尔用 类方法 (常用):常用于拼接字符串、把基本数据类型转换为字符类型 注意: string是不可变字符串,如果初始化没有赋值,就会为空,不可添加,只能更改指向 */ NSString *s1 = @"abcdef"; // 直接赋值 NSString *s2 = [[NSString alloc] initWithFormat:@"aabbcc%d",10]; //alloc、initxx NSString *s3 = [NSString stringWithFormat:@"age is %d", 10]; //类方法 s1 = @"abcdf"; // 直接修改指向 /* 2、对数据转换的操作 1》基本数据类型转换为字符串 2》常用结构体类型转换为字符串 3》oc和c字符串的转换 */ // 基本数据类型转换为字符串 NSString *numStr = [NSString stringWithFormat:@"age is %d", 10]; // int类型转换为字符串类型 NSString *doubleStr = [NSString stringWithFormat:@"高度是: %.2f", 10.0]; // double转换为字符串类型 // 结构体转换为字符串类型(是函数方法,不是string对象或类方法) CGRect rect = CGRectMake(10, 10, 10, 10); NSString *rectStr = NSStringFromRect(rect); // 结构体转换为字符串 (函数方法) //oc 和 c 字符串相互转换 NSString *s4 = [[NSString alloc] initWithUTF8String:"abcd"]; // 将c字符串转换为 oc字符串 NSString *s5 = [NSString stringWithUTF8String:"abcd"]; const char *str5 = [s3 UTF8String]; // oc转换为c字符串 /* 3、常用对其内容的操作 1》是否包含某个字符串->rangeOfString 2》遍历: length 、characterAtIndex 3》判断是否包含数字 */ // 判断字符串是否包含某个字符串(根据location是否是:NSNotFound) NSString *str = @"abcd efgh jk"; NSRange range = [str rangeOfString:@"bc"]; if (range.location == NSNotFound) { NSLog(@"没有"); }else{ NSLog(@"有"); } NSLog(@"-----字符串的遍历,并输出-------------"); // 遍历,characterAtIndex 根据索引值获取对应的字母,返回是字符类型 str = @"我们"; NSLog(@"length = %zd", str.length); // 结构是:2 (返回的是字符个数,而不是字节数) str = @"abcd 1 cd0e 2"; for (int i = 0; i<str.length; i++) { NSLog(@"%d ,%c",i,[str characterAtIndex:i]); } NSLog(@"-----判断是否有数字,并取出-------------"); // 判断是否包含数字 ——》根据asc码表种数字对应的值来判断 for (int i = 0; i<str.length; i++) { if ([str characterAtIndex:i] >= '0' && [str characterAtIndex:i] <= '9') { // 必须是字符类型,根据的是asc码表种数字对应的值来判断 NSLog(@"%c",[str characterAtIndex:i]); } } /* 4、对文件的操作 1》文件的读取 2》文件的写入 */ NSLog(@"-----读取操作文件(文本路径)-------------"); // 根据全路径,读取文本文件 NSString *path = @"/Users/an1911/Desktop/经典代码/Founcation框架的重点知识点/读取写入文件/readme.txt"; NSString *content = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil]; NSLog(@"\n%@",content); NSLog(@"-----读取操作网络资源(也可以读取文件)-------------"); /* URL : 资源路径 (可以读取文本文件,但资源路径不可以有中文) 协议头://路径 file://文本路径 http://网址 */ NSURL *url = [NSURL URLWithString:@"file:///Users/an1911/Desktop/经典代码/Founcation框架的重点知识点/读取写入文件/readme.txt"]; // 不可包含汉字,且必须是全路径,包括后缀名 NSLog(@"有中文的url = %@",url); // url为空 content = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil]; NSLog(@"有中文——》\n%@",content); url = [NSURL fileURLWithPath:@"/Users/an1911/Desktop/经典代码/Founcation框架的重点知识点/读取写入文件/readme.txt"]; // 如果确定是文本,可用这种方式() NSLog(@"fileURLWithPath——》地址:\nurl = %@",url); content = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil]; NSLog(@"fileURLWithPath——》读取的内容:\n%@",content); url = [[NSURL alloc] initWithString:@"file:///Users/an1911/Desktop/readme.txt"]; NSLog(@"无中文——》url = %@",url); content = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil]; NSLog(@"\n%@",content); url = [NSURL URLWithString:@"http://www.baidu.com"]; //content = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil]; // NSLog(@"\n%@",content); NSLog(@"-----写入操作文本文件(对象方法)-------------"); NSString *writeStr = @"JAVA1\nJAVA2"; // 使用\n作为写入的换行 [writeStr writeToFile:@"/Users/an1911/Desktop/write.txt" atomically:YES encoding:NSUTF8StringEncoding error:nil]; url =[NSURL fileURLWithPath:@"/Users/an1911/Desktop/write2.txt"]; // url = [NSURL URLWithString:@"file:///Users/an1911/Desktop/write2.txt"]; [writeStr writeToURL:url atomically:YES encoding:NSUTF8StringEncoding error:nil];
注意:输入的时候,中间使用空格、tab、回车作为间隔符合,直到输入完三个数据
然后按回车,结束输入,并赋值
*使用自己定义的间隔符合
<strong> int a= 1 ,b= 1, c =1; scanf(“%d,%d,%d”,&a,&b,&c); printf("=%d,%d,%d=",a,b,c); </strong>
注意:我们在输入数据的时候,就必须带上“,”作为数据间的间隔,如果使用其它间隔符合,会造成赋值的错误,如输入:11,12,13 ,然后回车就会把数据依次赋值
或3,□4,□5 ↙(输入a,b,c的值)
3,□□□4,□5 ↙(输入a,b,c的值)(把%d,看作一个整体)
不可:3□□□,4□,5
2》接收3个字符数据:(会有些莫名的错误——》把空格、tab、回车作为数据赋值,不以它们为分割符,以对应关系和字符个数结束判断依据)
<strong> char a ,b, c; scanf(“%c%c%c",&a,&b,&c); printf("<%c,%c,%c>",a,b,c); </strong>
注意:这里我们是默认没有间隔符,但是因为scanf在给char类型的数据赋值的是,会把空格、tab、回车或其它任何符合如“,”、“#”等都当作一个char数据赋值给后面的地址参数对应的变量。所以这里特别注意,如果我们输入数据的时候,以空格、tab、回车等作为间隔的时候,实际上是把它们赋值给了后面的变量地址对应的变量,这样我们的数据就会混乱,
输入: a回车b回车——》当第二个回车的时候,就会当作输入结束
输出:<a,
,b>
分析:这里会把输入的第一回车当作,第二个字符数据。当我们输入完三个字符数据后,我们再按回车的时候,系统会认为我们已经输入完了三个字符数据,从而结束输入,并赋值变量,把a赋值给变量a,回车赋值给b,把b赋值给b变量。(注意输入结束点,就是输入数据个数和格式符个数一致了)
正确输入:直接输入连续的字符,以回车结束
输入:abc回车
输出: <a,b,c>
所以,接收多个字符数据的时候,如果没有默认的分割符。我就直接输入对应个数的连续字符即可:
*使用间隔符
<strong>char a ,b, c; scanf("%c,%c,%c",&a,&b,&c); printf(“<%c,%c,%c>”,a,b,c);</strong>
输入:a,b,c 输出:a,b,c
注意一个细节:如果我们是在输入法是汉字的情节下,按亮caps lock键,我们看到的控制台输入的是小写,但输出的是大写。因为这是一个大写的键。所以切换为英文格式,这样我们看到输入是小写,输出的也是小写
难点:如果我们使用多个scanf接收数据的时候,前面遗留的数据会造成后面对字符数据接收的错误影响,所以一般还要在最后添加fflush(stdin);这个函数,清空输入流,后面介绍
6、接收字符串(c语言中必须使用字符数组作为接收变量,且要确定元素个数)
(1)、%s的基本使用——》不可接收带空格的字符串
1》单个%s的基本使用——》不需要使用&
注意1:
%s只是接收一个字符串,如果我们输入字符串的时候,中间有空格、tab,这两个默认的间隔符,其后面的字符不会赋值给地址对应的数组变量,而是当作无用数据给截去。且只有按回车键就会立刻结束输入
开始输入 tab、空格、回车,都是被scanf给忽略(先这样理解)
注意2:这样接收的字符串,不能包含空格。
2》多个%s的同时使用
<strong>char name1[10]; char name2[10]; scanf("%s%s", name1, name2); printf("%s,%s", name1, name2); //输入:abcdefg hijklmn (这里使用空格间隔) //输出:abcdefg,hijklmn</strong>
*接收多个字符串 ——》默认采用tab、空格、回车作为间隔符
(2)、使用扫描集,来接收字符串,上面已经介绍
*注意把空白符当作其赋值内容的一部分。而%s会把空白符合当作间隔符使用
7、难点问题
(1)、stdin中的缓冲区问题——》存在于多个scanf操作中(如果是单个scanf操作只会直接返回不影响)1》单个scanf函数,不存在缓存问题,只是不匹配,就直接返回
<strong>// 一个scanf,就算是为多个数据读取赋值操作,遇到错误信息会直接返回,而不会存在stdin的缓存现象,而引起的错误 int age; char c; scanf(“%d,%c”, &age,&c); printf("%d,%c", age, c); /* 输入:a回车 输出: 0, 分析:scanf函数首先读取到的字符a,和首个格式符%d进行匹配,结果不匹配直接返回。而没有给变量c赋值 */ </strong>
<strong>// 修改为多个scanf,这个时候就会有缓存引起问题 int age; char c; scanf("%d", &age); scanf("%c",&c); printf("%d,%c", age, c); /* 输入:a回车 输出: 0,a 分析:第一个sanf函数读取stdin数据,首先读到字符a,和其格式符号%d匹配,结果不匹配,直接返回,结束第一个scanf函数。但是字符”a“依然存在在stdin缓存中,当第二scanf执行的时候,stdin中有数据,scanf会直接执行,读取数据和%c匹配,满足匹配,把字母a赋值给了变量a(下面详解) */ </strong>
2》多个scanf函数,存在stdin缓存问题(会把stdin里面残留的数据和后面类型匹配并赋值)——》scanf("%*[^\n]");和scanf(“%*[^\n]%*c”);的使用和区别
注意看下面的错误情况:
<strong>// 不安全的写法,注意容易出错的地方 #include <stdio.h> int main(int argc, const char * argv[]){ int age = 1; char name[10]; scanf("%d", &age); scanf("%s", name); printf(“age =%d,name=%s", age, name); return 0; }/* 输入:abce回车 输出:age=1,name=abce 分析: 第一个scanf函数在执行的时候,从stdin中读取的数据是字符串,和%d类型不匹配,直接返回,结束函数 第二个scanf函数执行的是,查询到stdin 中有缓存的数据,会自动的执行(而不是等我们再次输出然后回车操作),读取到字符串abce和其类型符%s匹配,把从stdin中读取的字符串赋值给name,并在stdin缓存中清除读取到到内容 改错:我们只要在第一个scanf执行完毕后,清空在stdin中的缓存即可,两种方式 1、fflush(stdin)——》存在移植性问题,在vc6中有效。但是在xcode5中无效,不建议使用 2、使用自己编写的代码清空缓存 scanf(“%*[^\n]"); 清除一段字符串 scanf(“%*[^\n]%*c”);清除一段字符串和其后面的空白符号——》用在后面是为%c或字符集赋值的操作(它们把空白字符当作内容处理)*/</strong>
修改为如下正确写法:
<strong>//去除缓存的正确写法 #include <stdio.h> int main(int argc, const char * argv[]){ int age ; char name[10]; scanf("%d", &age); scanf("%*[^\n]"); scanf("%s", name); printf("%d,%s", age, name); return 0; } /* 输入:abc(回车) def(回车) 输出:0,def 分析,前的abc已经被scanf(“%*[^\n]”);,把stdin中的abc给清除了,没了影响了。但是这个时候stdin中还有abc输入完后的按的回车,在stdin中会出现对应的’\n“符号。但是%s默认以它为空白符号,不当作数据处理,所以无影响。但是如果后面是%c或字符集、%[],就必须在消除字符串后,再消除回车符号,使用scanf(“%*[^\n]%*c”); */ </strong>
scanf(“%*[^\n]%*c”); 清除回车的情况:
<strong>//清除回车符号的重要性 #include <stdio.h> int main(int argc, const char * argv[]){ int age ; char c; scanf("%d", &age); scanf("%*[^\n]"); scanf("%c", &c); printf(“<%d,%c>, age, c); return 0; } /* 输入:abc (回车) 输出:<0, > 不能为c输入数据的,出错分析: 第一个scanf,读取的字符串和%d不匹配直接返回,字符串依然存在再stdin中 然后第二个scanf函数,读取出了前面一段字符串,但不赋值,消除了abc字符串的缓存影响。但是再stdin中依然存在回车符号\n, 第三个scanf执行,stdin里有数据\n,读取并赋值,把\n赋值给c了 正确分析: 把第二个scanf修改为 scanf(“%*[^\n]%*c”);,就能为c输入字符数据 输入:abc (回车) d 输出:<0,d> 注意:我们每击打一下"Enter"键,向缓冲区发去一个“回车”(/r),一个“换行"(/n), */ </strong>
习题一:避免因错误输入造成的死循环
<strong>// 注意错误原因 #include <stdio.h> int main() { int a; int b; do{ printf("请输入数字:\n"); scanf("%d",&a); scanf("%d",&b); printf("a=%d, b=%d\n",a,b); }while(b!=11); printf("结束输入"); } /* 输入:a (回车) 结果:死循环 分析: stdin中一直有缓存数据a,导致scanf会直接读取并赋值操作,但是数据一直不匹配,所以a又一直存在在stdin的缓存中,导致死循环 */ </strong>
<strong>// 修改后的正确版本 #include <stdio.h> int main() { int a; int b; do{ printf("请输入数字:\n"); scanf("%d",&a); scanf("%*[^\n]"); scanf("%d",&b); scanf("%*[^\n]"); printf("a=%d, b=%d\n",a,b); }while(b!=11); printf("结束输入"); } /* 分析: scanf(“%*[^\n]”);会把前一个stdin中的不匹配的除了\n以外的数据都清除了。且%d不会把空格符号作为数据读取和赋值 */ </strong>
习题二、无法正确的输入字符数据情况(比例题2更多的判断,跟加安全)
<strong>// 不安全的写法 #include <stdio.h> int main() { int a; char b; do{ printf("请输入:\n"); scanf("%d",&a); scanf("%c",&b); printf("<a=%d, b=%c,b=%d>\n",a,b,b); }while(b!=’n'); printf("结束输入"); } /* 输入:11 (回车) 输出:<a=11, b= ,b=10> 请输入数字: 分析:回车赋值给b了 正确输入方式:(很容易输入出错) 输入:11c 回车:<a=11, b=c,b=99> */ <pre name="code" class="csharp">// 正确写法( scanf("%*c");;) #include <stdio.h> int main() { int a; char b; do{ printf("请输入:\n"); int num = scanf("%d",&a); if(1 == num ){ scanf("%*[ \t\n]”); //scanf(" “); 不安全 }else{ scanf("%*[^\n]%*c"); } scanf("%c",&b); scanf("%*[^\n]%*c"); printf("<a=%d, b=%c,b=%d>\n",a,b,b); }while(b!='n'); printf("结束输入"); } /* 输入:11 (回车) c 或:aa (回车) c 或 11 (tab)(回车) c 或 11 (空格)(tab)c 结果都会为,b赋值为c */ </strong>
习题三:使用 getchar() 清除所有缓存
<strong>int main(){ int i, c; for (;;) { // 无限循环 // 表示一直的数据输入,不管是否匹配,stdin缓存区都会有数据残留 if ( scanf("%d", &i) != EOF ) { //如果不按ctrl +d就会一直输入数据 // 循环如果c不是\n或文件最后,就一直使用getchar读取除缓冲区 while ( (c=getchar()) != '\n' && c != EOF ); } printf("%d\n", i); } return 0; } </strong>
习题四、用“空格符”来处理缓冲区残余信息
<strong>// 不安全写法 #include <stdio.h> int main() { char str[4]; for(int i=0; i<5; i++){ scanf("%c",&str[i]); } printf("%s",str); return 0; } /* 输入: a b c d e(回车) 输出: a b c 或 输入:a(回车) b(回车) c(回车) 输出:a b c */ </strong>
<strong>// 正确写法 #include <stdio.h> int main() { char str[4]; for(int i=0; i<5; i++){ scanf(" %c",&str[i]); } printf("%s",str); return 0; } /* 分析 scanf(" %c",&str[i]);会把所以的空格符号都给读取出来,清空stdin的缓存 和:scanf("%*[ \t\n]”);效果一样 输入:a b c d e(回车) 输出;abcde */ </strong>
(2)、多个%d赋值中,回车的不同情况(输入非法数据,回车立刻结束输入)
<strong>//多个%d赋值,输入数据的时候,回车符号的特殊情况 int age; int age1; int age2; char c; scanf("%d%d%c", &age1, &age2,&c); printf(“%d,%d,%c", age1, age2,c); // 或 int age1; int age2; scanf("%d", &age1); scanf("%d", &age2); /* 分析: 如果是匹配的数据,回车、空格、tab键都会当作间隔符号使用(多个%d的情况下) 输入:11回车 或输入 11 12 c 12回车 输出:11,12 输出:11,12 分析:这里因为是匹配数据,所以回车可以当作间隔符号使用,但是这里注意char类型接收的类型中包括所有空白符号,所以char变量c不是没有输出,而是输出的是“\n”(注意理解) 如果输入的是不匹配数据,空格和tab还是可以输入,而回车会直接导致执行完毕,而不再是间隔符(多个%d的情况下) 输入:a □11□□□□ c回车 输出:1,2, 分析:输入的时候采用空格和tab键为间隔符合,当我们认为输入完三个数据的时候,按回车,结束输出。执行scanf函数读取并赋值。但是读取的第一个数据是a,而类型符是%d不匹配直接返回,结束scanf函数 输入:a回车 输出:1,2, 分析:如果我们输入的是不匹配的数据,如果按回车,会直接执行scanf完毕。而不再以回车键为分隔符合,然后输入另外的数据。和上面的第一种情况注意区分 */ </strong>
8、经典代码总结——》在实现功能的基础上,精简代码、保证数据安全性
(1)、接收用户输入两个数,并计算它们的和值(这个功能很好实现,难点在于如何保证用户如果输入不正确的数据,程序能够判断出并给出相应的处理1、不输出错误数据,2、保证继续循环告诉用户输入数据,而不是只执行一次,3、保证不死循环)
<strong>#include <stdio.h> int main() { int a,b; // 接收数据的变量 int count; // scanf返回的成功项 char isErro = 'N'; //提示语音的判断 while(count != 2){ if('N' ==isErro){ printf("请输入两个数(使用逗号隔开)\n"); }else if('Y' == isErro){ printf("输入错误数据,请重新输入两个数(使用逗号隔开)\n"); } count = scanf("%d,%d", &a, &b); scanf("%*[^\n]%*c"); // 清除stdin中的缓存数据(如果是给%c和%[]赋值,需要更多的判断) if(2 == count){ //如果两个变量数赋值成功 int sum = a+b; printf("和是:%d\n",sum); }else{ isErro = 'Y'; } } return 0; } </strong>
<strong>// 精简的代码 #include <stdio.h> int main() { int a,b,c; /*计算a+b*/ while(scanf("%d,%d",&a,&b)!=2)scanf("%*[^\n]%*c"); c=a+b; printf("%d+%d=%d",a,b,c); return 0; } </strong>
补充:
对EOF的基本认识
while(scanf(“%d”,&n)!=EOF));
标题那段代码的意思是,输入Ctrl+z终止循环(这是在Windows下,在Unix环境下是Ctrl+d)。如果你输入字符a,而循环体里又没有getchar之类读字符的函数,就会死循环,因为a会一直留在输入缓冲区中。要想在输入错误的情况下终止把 !=EOF 去掉就行了,即成功输入的个数为0的情况下推出循环。
相关文章推荐
- ios开发-c语言之scanf函数和基本运算的学习
- 【iOS-cocos2d-X 游戏开发之十三】详细讲解在Xcode中利用预编译并通过Jni调用Android的Java层代码(cocos2dx里访问调用Android函数)!
- 【iOS-cocos2d-X 游戏开发之十三】详细讲解在Xcode中利用预编译并通过Jni调用Android的Java层代码(cocos2dx里访问调用Android函数)!
- iOS开发系列--详细讲解C语言之存储方式和作用域
- iOS开发学习笔记 2-9 C语言部分 内存分配函数 函数指针 指针函数 void*
- IOS开发---C语言-⑥printf函数的返回值
- IOS开发---C语言-㉓static和extern对函数和变量的作用
- Linux中C语言open函数打开或创建文件详细讲解
- IOS开发之UITableView 详细讲解
- iOS开发学习笔记 2-9 C语言部分 内存分配函数 函数指针 指针函数 void*
- C语言scanf函数详细解释
- C语言scanf函数详细解释
- 黑马程序员——零基础学习iOS开发——04 c语言:基本运算、流程控制、函数
- 【零基础学习iOS开发】【02-C语言】10-函数
- C语言scanf函数详细解释
- Linux中C语言open函数打开或创建文件详细讲解
- IOS开发---C语言-⑬函数与指针
- (转)---C语言中格式化日期时间asctime()函数详细讲解
- 【零基础学习iOS开发】【02-C语言】11-函数的声明和定义
- C语言scanf函数详细解释