iOS開發——c語言——scanf函式詳細講解
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函式結束情況
*每一個格式符均有對應的資料,然後按回車後結束
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修飾表示longprintf(“<%Lf>”,a);// 輸出也要使用Ldouble a;scanf(“%lf",&a); // 使用小寫l,表示普通doule
int num = 0;2》引數是變數的地址,使用&取地址符(陣列除外,不用&)
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的情況下推出迴圈。