羽夏閒談—— C 的 scanf 的高階用法

寂靜的羽夏發表於2022-02-10

前言

  今天看到博友發了個有關scanf的使用的注意事項,就是討論緩衝區殘存資料的問題,用簡單的程式碼示例複述一下:

#define _CRT_SECURE_NO_WARNINGS

#include <stdlib.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
    int a;
    char b;
    scanf("%d", &a);
    scanf("%c", &b);
    printf("a = %d , b = %d\n", a, b);
    system("pause");
    return 0;
}

  你或許碰到這個輸出:

5
a = 5 , b = 10
請按任意鍵繼續. . .

  我明明想輸入個5,然後回車輸入下一個字元,但是,回車符也是個字元,會被scanf進去,絕大多數人的解決方案就是提前把這個字元讀取走,但是如果緩衝區的東西太多的話,需要加個迴圈,才能處理,下面我來介紹scanf的高階用法,之間研究過,忘記在哪裡看到的,這個是我總結的,那些基礎用法自己看看書就行了。

清空快取區

  我先把清空緩衝區的程式碼放上,因為後面的程式碼都會用到,至於為什麼後面會有詳細介紹:

scanf("%*[^\n]"); //清除到回車符的所有字元
scanf("%*c");  //清除回車符

指定輸入長度

  我們都知道scanf可以指定小數位數和長度,如下是程式碼示例:

#define _CRT_SECURE_NO_WARNINGS

#include <stdlib.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
    int n;
    float f;
    char str1[23];
    scanf("%2d", &n);
    //清空緩衝區程式碼{
    scanf("%*[^\n]");
    scanf("%*c");
    //}
    scanf("%5f", &f);
    //清空緩衝區程式碼{
    scanf("%*[^\n]");
    scanf("%*c");
    //}
    scanf("%5s", str1);
    puts("執行後:");
    printf("n=%d, f=%g, str=%s\n", n, f, str1);
    
    system("pause");
    return 0;
}

  如下是輸入和輸出結果:

5653 12
2.56458452 2356.9999
helloworld
執行後:
n=56, f=2.564, str=hello
請按任意鍵繼續. . .

  看到沒,清空緩衝區的程式碼有效果了,如果沒有這行清空緩衝區的程式碼,就會成這樣子:

5653 12
執行後:
n=56, f=53, str=12
請按任意鍵繼續. . .

  這就是清除緩衝區的作用。

單範圍匹配

  不要驚訝,scanf也是支援類似正規表示式的功能的,我們用如下程式碼進行演示:

#define _CRT_SECURE_NO_WARNINGS

#include <stdlib.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
    char str2[30];
    scanf("%[abcd]", str2); //%[abcd]表示只要字串只有 a,b,c,d 範圍內就匹配
    puts("執行後:");
    printf("%s\n", str2);
    system("pause");
    return 0;
}

  如下是輸入和輸出結果:

babccbaxyz
執行後:
babccba
請按任意鍵繼續. . .

  程式碼中的註釋可能說的不明白,這裡我長篇大論一下:字串從開頭開始匹配,必須字串只有abcd中這四個字元任意一個才有效,如果開頭沒有這四個字元,則匹配為空。

多範圍匹配

  既然支援單範圍了,肯定也支援多範圍,什麼是多範圍匹配可以先看看一些基礎示例,如果會正規表示式的話很容易懂:

  • %[a-z]表示讀取 abc...xyz 範圍內的字元,也即小寫字母;
  • %[A-Z]表示讀取 ABC...XYZ 範圍內的字元,也即大寫字母;
  • %[0-9]表示讀取 012...789 範圍內的字元,也即十進位制數字;
  • %[a-zA-Z]表示讀取大寫字母和小寫字母,也即所有英文字母;
  • %[a-z-A-Z0-9]表示讀取所有的英文字母和十進位制數字;
  • %[0-9a-f]表示讀取十六進位制數字

  如果經常使用的話也就這些型別,如下是程式碼示例:

#define _CRT_SECURE_NO_WARNINGS

#include <stdlib.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
    char str3[30];
    scanf("%[a-zA-Z]", str3); //只讀取字母
    puts("執行後:");
    printf("%s\n", str3);
    system("pause");
    return 0;
}

  如下是輸入和輸出結果:

abcXYZ123abcXYZ123
執行後:
abcXYZ
請按任意鍵繼續. . .

不匹配某些字元

  既然有匹配的字元,肯定有不想匹配的字元。對於不匹配某些字元,scanf允許我們在%[ ]中直接指定某些不能匹配的字元,具體方法就是在不匹配的字元前面加上^,給幾個例子:

  • %[^\n]表示匹配除換行符以外的所有字元,遇到換行符就停止讀取;
  • %[^0-9]表示匹配除十進位制數字以外的所有字元,遇到十進位制數字就停止讀取。

  如下是程式碼示例:

#define _CRT_SECURE_NO_WARNINGS

#include <stdlib.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
     char str11[30], str12[30];
    scanf("%[^0-9]", str11);

    scanf("%*[^\n]");
    scanf("%*c");           //清空緩衝區
    
    scanf("%[^\n]", str12); //等效為gets()
    puts("執行後:");
    printf("str1=%s \nstr2=%s\n", str11, str12);
    system("pause");
    return 0;
}

  如下是輸入和輸出結果:

abcXYZ@#87edf
Cnblog wingsummer
執行後:
str1=abcXYZ@#
str2=Cnblog wingsummer
請按任意鍵繼續. . .

丟棄資料

  scanf還允許把讀取到的資料直接丟棄,不往變數中存放,具體方法就是在%後面加一個*,如下是程式碼示例:

#define _CRT_SECURE_NO_WARNINGS

#include <stdlib.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
    int nn;
    char str[30];
    scanf("%*d %d", &nn);
    scanf("%*[a-z]");
    scanf("%[^\n]", str);
    puts("執行後:");
    printf("n=%d, str=%s\n", nn, str);
    system("pause");
    return 0;
}

  如下是輸入和輸出結果:

100 999abcxyzABCXYZ
執行後:
n=999, str=ABCXYZ
請按任意鍵繼續. . .

相關文章