羽夏閑談—— C 的 scanf 的高級用法

前言

  今天看到博友發了個有關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
請按任意鍵繼續. . .