程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> C語言scanf函數奇遇記

C語言scanf函數奇遇記

編輯:關於C語言

看《The C Programming Language》中關於scanf函數部分時隨意敲了幾行代碼,本以為簡單的不得了,都有點“不屑於”敲,卻沒想到這一敲竟然敲出個不小的問題,涉及到好多東西啊,哈哈!下面把我這次的經歷和大家分享一下,希望也能對大家有所幫助。
    一、代碼實例
    我當時敲的代碼:
    點擊(此處)折疊或打開
    #include<stdio.h>
    int main()
    {
    int a;
    int b;
    char mon[20];
    int count;
    count = scanf("%d,%s,%d", &a,mon,&b);
    printf("%d,%s,%d\n",a,mon,b);
    printf("%d\n",count);
    return 0;
    }
    運行結果:
    ocean@ocean-desktop:~/桌面$ ./re
    12,fefe,45   /*這是我的輸入*/
    12,fefe,45,10359588
    2
    結果看起來挺像我們想要的結果的,只是最後多了個奇怪的數字;但仔細看下count的值我們就納悶了,怎麼是2不是3呢?怎麼scanf只讀了兩個值?到底怎麼回事呢?先用gdb調試一下吧,看看a和mon裡都是些什麼。
    二、GDB調試情況
    (gdb) p a
    $1 = 12
    (gdb) p mon
    $2 = "fe,45\000\377\277\245\324\025\000\060\340\021\000K\205\004\b"
    明白了吧?原來fe,45作為一個整體被存到mon裡了,b根本沒讀到值,顯示了個原內存裡的亂七八糟的數值(不相信的話可以在程序開頭給b賦個值,最後結果肯定是輸出當初賦的值,因為根本沒有給b讀入新的值),scanf真的只讀了兩個值,所以count顯示2。那為什麼會這樣呢?讓我們來看看scanf函數的相關信息吧。
    三、scanf函數工作原理
    scanf()是從輸入流緩沖區中讀取值的,而並非從鍵盤(也就是終端)緩沖區讀取。往輸入流緩沖區送數據是遇到回車(\n)而結束的,這個\n會一起讀入輸入流緩沖區。scanf() 開始讀取輸入以後,會在遇到的第一個空白字符空格(blank)、制表符(tab)或者換行符(newline)處停止讀取。
    格式控制字符串中有普通字符(非格式字符)時,這些字符作為輸入數據的分隔符,在scanf函數讀入數據時自動去掉。
    scanf()格式控制字符串中如果使用%s說明符,那麼空白字符以外的所有字符都是可以接受的,所以scanf() 跳過空白字符直到遇到第一個非空白字符,然後保存再次遇到空白字符之前的所有非空白字符。這就意味著%s使scanf() 讀取一個單詞,也就是說,一個不包含空白字符的字符串。
    好,讓我們分析下上述的結果是如何出現的吧。
    四、原因分析
    首先,scanf()跳過空白字符(這裡沒有,因為第一個字符就是1)直到遇到一個非空白字符1,然後繼續讀2,讀到逗號這個非數字符號時scanf知道整數讀完了,將12賦給a,此時輸入流緩沖區中第一個開頭的字符是逗號;scanf繼續讀,讀到逗號與格式控制字符串的逗號匹配,pass;從f繼續讀,一直讀到下一個空白符——我們結束時敲的回車(scanf自動把這個回車符去掉了,沒有送到字符串裡),字符串讀完了,此時輸入流緩沖區裡第一個開頭的字符是我們敲的回車符;繼續讀,回車符與格式控制字符串裡的逗號不批配,讀取失敗,不讀了。   綜上所述,scanf確實只讀了一個整數和一個字符串,返回值是2。www.2cto.com
    那有什麼辦法實現用逗號作為間隔符的情況呢?下面提供兩種方法:
    五、解決方法
    法1:
    scanf("%d,%[^,],%d", &a,mon,&b);
    printf("%d,%s,%d\n",a,mon,b);
    相關知識:scanf中一種很少見但很有用的轉換字符:[…]和[ ^…]
    %[…]如果輸入的字符屬於方括號內字 符串中某個字符,那麼就提取該字符;如果一經發現不屬於就結束提取。%[^…]如果一經發現輸入的字符屬於方括號內字符串中某個字符,那麼就結束提取;如果不屬於就提取該字符。這兩種方法會自動加上一個字符串結束符到已經提取的字符後面。例如:
    點擊(此處)折疊或打開
    #include<stdio.h>
    main()
    {
    char strings[100];
    scanf("%[1234567890]",strings);
    printf("%s",strings);
    return 0;
    }
    運行,輸入:1234werew後,結果是:1234。
    采用這種方法,讀完fefe後遇到逗號便結束字符串的讀取,繼續讀時輸入流緩沖區的逗號與格式控制字符串中逗號剛好匹配,成功!
    法2(不夠徹底):
    scanf("%d,%s ,%d", &a,mon,&b);   /*注意%s後面有個空格 */
    printf("%d,%s,%d\n",a,mon,b);
    並且在輸入時加個空格
    12,fefe ,45   /*fefe和逗號之間加個空白*/
    相關知識:當scanf()格式控制字符串中出現空白時,表示取數時跳過任何空白。
    scanf讀到fefe後的空格後結束字符串的讀取,此時輸入流緩沖區第一個字符為空格;繼續讀,由於格式控制字符串裡有個空格,所以讀取時會跳過任何空白(不信可以在fefe後面多敲幾個空白試試,全都跳過,甚至連回車都跳過),讀到逗號匹配成功。 

摘自  家裡蹲博客 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved