複製鏈接
請複製以下鏈接發送給好友

scanf

鎖定
scanf()是C語言標準庫中的函數,用於從標準輸入流stdin中按照指定的格式讀取數據,並將其存儲到由額外參數指向的位置。調用scanf()函數時,需要提供格式字符串作為第一個參數,該字符串包含了讀取數據的格式信息。額外參數應是指向已分配對象的指針,其類型由格式字符串中的格式説明符指定。
中文名
格式輸入
外文名
Scan Format
外語縮寫
scanf
應用學科
計算機科學
軟件語言
C/C++
屬    性
標準庫函數

scanf函數原型

int scanf(const char * restrict format,...);
函數scanf()是一個通用輸入子程序,從標準輸入流stdin [1]  (通常是指鍵盤輸入)中讀取內容,並將它們保存在相應地址的變量中。在調用scanf()函數之前,通常需要包含頭文件stdio.h,通過 #include <stdio.h> 提前聲明。

scanf參數

格式字符串 format:指定了輸入數據的格式。該字符串包含了格式説明符,用於解析輸入中的數據,並根據這些説明符將數據存儲到後續參數所指向的位置。
關鍵字restrict:表示指針format所指向的內存區域在函數作用域內不會被其他指針以其他直接或間接的方式修改。
可變參數列表 ...:表示函數接受的參數數量不定,參數的類型和數量取決於函數的實際調用方式以及開發者的設計,這些參數用於存儲根據格式字符串指定格式解析後的數據。

scanf返回值

返回值類型為int。
返回值表示函數執行成功讀取的數據項的數量,在讀取數據時遇到’文件結束’或讀取錯誤時返回EOF

scanf功能示例

#include <stdio.h>                                                        
int main() {                                                              
    int age;                                                               
    float height;                                                          
    char name[50];                                                        
    printf("請輸入您的年齡、身高(米)和姓名:");                          
    scanf("%d%f%s", &age, &height, &name);                               
    printf("您的姓名是:%s,年齡是:%d歲,身高是:%.2f米\n", name, age, height);                                                                     
    return 0;                                                               
}    
在該示例中,scanf() 函數根據格式字符串 %d %f %s從標準輸入中讀取一個整數、一個浮點數和一個字符,然後分別將它們存儲到 age、height和name變量中。在scanf()函數中,每個格式説明符後面都有一個相應類型的參數,這些參數通常是使用取地址操作符 & 獲取的變量的地址 [2]  。因此,&age表示整數變量age的地址,&height表示浮點數變量height的地址,而 &name則表示字符串變量 name的地址。
注意:在scanf()函數中,當格式説明符連續出現時,如%d%d%d,輸入時不能用逗號分隔,只能使用空白字符(例如空格、製表符或回車鍵)來分隔輸入的整數數據。
舉例來説,要求 %d%d%d 這樣的格式字符串時,輸入數據應該是像這樣的格式:“2 3 4”或“2(tab)3(回車)4”。如果格式字符串中包含逗號(,),例如 %d,%d,%d,則在輸入數據時必須使用逗號來分隔整數值,其格式為:“2,3,4”。

scanf格式指令説明

在scanf()函數中,格式字符串format包含一系列格式指令 [3] 
1、 * :指示scanf()在讀取輸入數據時將其丟棄,而不會將其存儲到任何變量中。例如,如果格式字符串為 %*d%d,則 `scanf` 將跳過第一個整數輸入,但會將第二個整數輸入存儲到對應的變量中。這樣,第一個整數值將被忽略,而第二個整數值將被讀取並處理。
2、 域寬:以一個非零的十進制整數形式表示該格式指令最多讀入的字符數。例如,%9s 表示要讀取的字符串最多包含9個字符(不包括字符串結尾的空字符'\0')。
3、 格式説明符:
格式説明符
描述
c
讀入域寬指定的數目個字符組成的字符序列,如果省略寬度則讀入單字符。如%c或%1c讀入單字符,%2c讀入兩個字符,以此類推。
s
讀入一個字符序列,後面會加上空字節,遇到空白字符(\t \r \n 空格等)完成讀取。
d
讀入可選有符號(可選有符號表示輸入時可以帶符號也可以不帶符號,不帶符號則視為非負)十進制整數。輸入格式應該像strtol函數的base實參為10調用時識別的字符序列一樣。
u
讀入無符號符號十進制整數。輸入格式應該像strtol函數的base實參為10調用時識別的字符序列一樣。
i
讀入可選有符號整數。輸入格式應該像strtol函數的base實參為0調用時識別的字符序列一樣。
a,e,f,g,A,E,F,G
讀入可選有符號浮點數,輸入格式應該像strtod函數識別的字符序列一樣。
o
讀入可選有符號八進制整數。輸入格式應該像strtoul函數的base實參為8調用時識別的字符序列一樣。
x,X
讀入可選有符號十六進制整數。輸入格式應該像strtoul函數的base實參為16調用時識別的字符序列一樣。
p
讀入一個指針值。讀入的字符序列應該與fprintf的%p產生的字符序列形式相同。
n
不讀入任何字符,而是把到該位置已讀入的字符數存儲到與之對應的int指向的位置。本轉換説明符如果帶有*或者域寬信息(如:%*n或%3n等),則後果是未定義的。
4、 長度修飾符與格式説明符的搭配使用情況:
長度修飾符
格式説明符
含義
hh
d, i, o, u, x, X, n
匹配 signed char 或 unsigned char 數據
h
d, i, o, u, x, X, n
匹配 short int 或 unsigned short int 數據
l
d, i, o, u, x, X, n
匹配 long int 或 unsigned long int 數據;匹配 wchar_t 數據(與 c, s, [ 配合使用)
l
a, A, e, E, f, F, g, G
匹配 double 數據
ll
d, i, o, u, x, X, n
匹配 long long int 或 unsigned long long int 數據
j
d, i, o, u, x, X, n
匹配 intmax_t 或 uintmax_t 數據
z
d, i, o, u, x, X, n
匹配 size_t 數據(或相應的有符號整型數據)
t
d, i, o, u, x, X, n
匹配 ptrdiff_t 數據(或相應的無符號整型數據)
L
a, A, e, E, f, F, g, G
匹配 long double 數據
例如,%hd用於讀取short int類型的整數,%lf用於讀取double類型的浮點數,%ld用於讀取long int類型的整數。
注意:如果長度修飾符與格式説明符不匹配則引起未定義的行為 [4] 

scanf空白和非空白字符

scanf空白字符

指在輸入流中表示空白的字符,包括空格(space)、製表符(tab)和換行符。
在scanf()函數中,空白字符的作用是使函數在讀取輸入時跳過一個或多個空白字符,而不將其計入輸入的數據中。這樣可以幫助忽略用户輸入中的不必要的空格、製表符或換行符,使輸入更加靈活。

scanf非空白字符

即除空白字符外的其他字符。當 scanf()函數在遇到一個非空白字符後,會忽略之前的空白字符,並從這個非空白字符開始進行輸入操作。

scanf注意事項

1、 格式串按照從左到右的順序,將格式説明符依次與變量表中的變量匹配。
2、 用於保存讀入值的變量必須是指針,即相應變量的地址。
3、 雖然空格、製表符和新行符在 scanf() 函數中被視為域分隔符,但在單字符讀取操作中,它們會被當作普通字符處理。
例如,如果對輸入流 "x y" 使用 scanf("%c%c%c",&a,&b,&c),結果會是:字符 'x' 存儲在變量 a 中,空格 ' ' 存儲在變量 b 中,字符 'y' 存儲在變量 c 中。
4、 注意控制串中的其他字符,包括空格、製表符和新行符,會被用於匹配和丟棄輸入流中的字符,匹配到的字符都會被忽略。
例如,對於輸入流 "10t20" 調用 scanf("%dt%d",&x,&y),結果:整數 10 存儲在變量 x 中,整數 20 存儲在變量 y 中,字符 't' 因為在控制串中,因此被忽略。
5、 ANSI C 標準向scanf()函數引入了一種名為掃描集(scanset)的新特性。掃描集定義了一個字符集合,scanf()可以從輸入中讀取符合集合中字符的內容,並將其賦值給相應的字符數組。掃描集由一對方括號中的字符定義,左方括號前需加上百分號。例如,下面的掃描集允許 scanf() 讀取字符 A、B 和 C: %[ABC]。
在使用掃描集時,scanf()會連續讀取集合中的字符,直到遇到不在集合中的字符為止,然後將這些字符放入相應的字符數組中,並以 null 結尾形成字符串。
另外,使用^字符可以表示補集,即除了指定集合中的字符之外的其它字符。例如,掃描集 %[^abc],^ 字符位於方括號的開頭,表示構建了字符 a、b 和 c 的補集,scanf()將接收除了 a、b 和 c 之外的所有字符。
對於許多實現來説,用連字符可以説明一個範圍(ISO C99標準沒有規定)。例如, %[A-Z] 表示 scanf() 接收字母 A 到 Z。
注意:掃描集是區分大小寫的。因此,如果需要區分大小寫字符,應該分別指定大寫和小寫字母。
高版本的 Visual Studio 編譯器中,scanf()被認為是不安全的,被棄用,應當使用scanf_s()代替scanf()。
6、 Scanf()函數中沒有類似printf的精度控制。如:scanf("%5.2f",&a);是非法的。不能企圖用此語句輸入小數為2位的實數。

scanf問題舉例

scanf問題一

如何讓scanf()函數正確接受有空格的字符串?如: I love you!
#include <stdio.h>                                                            
int main() {                                                                   
    char str[80];                                                              
    scanf("%s", str);                                                         
    printf("%s", str);                                                        
    return 0;                                                                 
}  
輸入:
I love you!
輸出:
I
上述程序並不能達到預期目的。因為scanf()掃描到"I"後面的空格就認為對str的掃描結束(空格沒有被掃描),並忽略後面的" love you!"。改動上面的程序來驗證一下:
#include <stdio.h>                                                                
#include <windows.h>                                                             
int main(void){                                                                   
    char str[80], str1[80], str2[80];                                           
    scanf("%s",str);/*此處輸入:I love you!*/                                    
    printf("%s\n",str);                                                          
    Sleep(5000);/*這裏等待5秒,告訴你程序運行到什麼地方*/                       
    scanf("%s",str1);/*這兩句無需你再輸入,是對stdin流再掃描*/                  
    scanf("%s",str2);/*這兩句無需你再輸入,是對stdin流再掃描*/                  
    printf("%s\n",str1);                                                         
    printf("%s\n",str2);                                                         
    return 0;                                                                     
}     
輸入:
I love you!
輸出:
I
love
you!
由上述代碼可知,殘留的信息 love you是存在於stdin流中,而不是在鍵盤緩衝區中。scanf() 函數可以藉助%[]格式控制符,程序如下所示:
#include <stdio.h>                                                            
int main() {                                                                   
    char str[80];                                                              
    scanf("%[^\n]", str); /*scanf("%s",str);不能接收空格符*/                
    printf("%s\n", str);                                                       
    return 0;                                                                  
}    

scanf問題二

如何解決鍵盤緩衝區殘餘信息問題?
#include <stdio.h>                                                            
int main() {                                                                   
    int a;                                                                     
    char c;                                                                    
    while(c != ‘N’){                                                                    
        scanf("%d", a);                                                       
        scanf("%c", c);                                                       
        printf("a=%dc=%c\n", a,c);                                                       
    }                                                                          
    return 0;                                                                  
}   
scanf("%c", &c);語句不能正常接收字符,原因是在輸入字符時,按下回車鍵會在輸入緩衝區中留下一個換行符'\n',而scanf("%c", &c);會讀取並處理這個換行符,導致無法正確接收字符。
解決方案:在兩個scanf()函數之間加入getchar()來處理這個換行符。

scanf問題三

如何處理輸入類型與格式化字符串不匹配導致stdin流的阻塞?
#include <stdio.h>                                                            
int main() {                                                                   
    int a=0, b=0, c=0, ret=0;                                                
    ret = scanf("%d%d%d ", &a, &b, &c,);                                     
    printf ("第一次讀入數量:%d\n", ret);                                    
    ret = scanf("%c%d%d ", &a, &b, &c,);                                    
    printf("第二次讀入數量:%d\n", ret);                                      
    return 0;                                                                  
}       
輸入:
1 b 2 
輸出:
第一次讀入數量:1 
第一個scanf("%d%d%d ", &a, &b, &c)試圖讀取三個整數,但輸入中第二個元素為b(非整數),因此只成功讀取一個整數1,返回值為1。b和2仍留在輸入緩衝區中。
輸入:
6
輸出:
第二次讀入數量:3  
第二個scanf("%c%d%d ", &a, &b, &c)讀取一個字符和兩個整數。它首先從緩衝區讀取字符b賦給變量a(ASCII值98),然後讀取緩衝區中的整數2,以及新輸入的整數6。
解決方案:在scanf()函數後使用fflush(stdin)函數清空輸入緩衝區以避免這類問題。修改後的程序如下:
#include <stdio.h>                                                            
int main() {                                                                   
    int a=0, b=0, c=0, ret=0;                                                  
    ret = scanf("%d%d%d ", &a, &b, &c,);                                       
    fflush(stdin);                                                             
    printf ("第一次讀入數量:%d\n", ret);                                       
    ret = scanf("%c%d%d ", &a, &b, &c,);                                       
    fflush(stdin);                                                             
    printf("第二次讀入數量:%d\n", ret);                                        
    return 0;                                                                  
}  
輸入:
1 b 2 
輸出:
第一次讀入數量:1  
fflush(stdin)函數清空輸入緩衝區,移除未處理的數據。
輸入:
1 3 6  
輸出:
第二次讀入數量:3  
由於緩衝區已清空,新的輸入完整被第二個scanf()處理。

scanf問題四

如何處理scanf()函數誤輸入造成程序死鎖或出錯?
#include <stdio.h>                                                            
int main() {                                                                   
    int a, b, c;                                                              
    scanf("%d%d", &a, &b);                                                    
    c = a + b;                                                                  
    printf("%d+%d=%d\n", a, b, c);                                           
    return 0;                                                                  
}    
當程序接受正確的輸入值a和b時,能夠順利執行並輸出結果。
一旦用户輸入非整數或格式不正確的數據,scanf()可能無法正確解析輸入,導致程序不能繼續前進或錯誤計算。
解決方法:利用scanf()函數的返回值來判斷成功讀取的變量數量。如果scanf()函數沒有成功讀取所有預期的變量,其返回值將小於請求的變量數量。
以下示例展示瞭如何通過循環和錯誤處理來確保正確輸入:
#include <stdio.h>                                                            
int main() {                                                                   
    int a, b, c;                                                              
    while(scanf("%d%d", &a, &b) != 2){                                       
          while( getchar() != '\n');  /*清空輸入緩衝區*/                    
          printf("輸入無效,請輸入兩個整數並用空格分離。\n")                   
    }                                                                             
    c = a + b;                                                                 
    printf("%d+%d=%d\n", a, b, c);                                             
    return 0;                                                                  
}      
補充:fflush(stdin)這個方法在GCC下不可用(在VC6.0下可以),可以使用while( getchar() != '\n')代替fflush(stdin)進行緩存清理。

scanf發展

scanf()函數作為C語言中的標準輸入函數,在早期的編程中扮演着重要的角色。它的設計初衷是為了方便從標準輸入流中讀取各種數據類型,並將其賦值給對應的變量。隨着C語言的發展,scanf()函數逐漸成為了C編程中常用的輸入方式之一。
然而,隨着編程語言的發展和用户需求的變化,scanf()函數也暴露出了一些不足之處。其中包括對輸入格式的嚴格要求、容易產生輸入錯誤導致程序異常等問題。特別是在面對複雜的輸入場景時,scanf()函數的使用變得繁瑣而不靈活。
在C++語言中,儘管scanf()函數仍然存在並可用,但由於C++提供了更加強大和易用的輸入輸出庫iostream,大多數程序員更傾向於使用cin()和cout()進行輸入輸出操作。cin()提供了更友好的接口和更好的錯誤處理機制,使得輸入操作更加簡便和安全。
綜上所述,儘管scanf()函數在C語言和一些特定的編程場景中仍然被廣泛使用,但隨着編程語言的發展和用户需求的變化,它的地位逐漸被更加現代化和便捷的輸入方式所取代。
參考資料