C/C++下scanf的%匹配以及過濾字符串問題


最近在寫一個測試的小程序,由於用到了sscanf函數對字符串進行標准讀入,而sscanf在很多方面都與scanf比較相像,於是對scanf進行了一番測試,遇到了一系列基礎性的問題,惡補基礎的同時也體現了自己的薄弱。

話不多說,直接附上問題吧。

問題描述

#include<stdio.h>
#include<string.h>

int main()
{
        while(1)
        {
                char str[256]={0};
                int a=0;
                char b='\0';
                printf("please input the num:\n");
                scanf("%[^,],%[^,],%d",str,&b,&a);
                printf("input is str: %s  b: %c  a: %d And str length is %u, the first character is %u\n",str,b,a,strlen(str),str[0]);
                //gets(str);
        }
        return 0;
}

在這段代碼里,如果我初始輸入"a,a,0",輸出正確。但輸入",,0"的話,接下來會自動不需要輸入然后循環着不斷的輸出,想問一下這個是什么原因。還有一個是我將代碼改成了如下:

復制代碼
#include<stdio.h>
#include<string.h>

int main()
{
        while(1)
        {
                char str[256]={0};
                int a=0;
                char b='\0';
                printf("please input the num:\n");
                scanf("%*[\n\r\t]%[^,],%[^,],%d",str,&b,&a);
                printf("input is str: %s  b: %c  a: %d And str length is %u, the first character is %u\n",str,b,a,strlen(str),str[0]);
                //gets(str);
        }
        return 0;
}
復制代碼

本來目的是過濾輸入的回車,其中%*是過濾滿足條件的字符,但是如果直接輸入"a,a,0",也會出現不需要輸入然后不斷地循環輸出的現象。

 

問題解答

scanf里面的任何%都至少要匹配1個字節,所以連續兩個逗號是匹配不出任何東西的。注意這里,哪怕是%*...都是只要要匹配一個字符的,匹配不了的就是失敗。而死循環的原因,是因為流里面一個字節都沒有被接受,所以每次循環面對一摸一樣的流。scanf每次都失敗。

示例:

 1 #include<stdio.h>
 2 #include<string.h>
 3 
 4 int main()
 5 {
 6         while(1)
 7         {
 8                 char str[256]={0};
 9                 int a=-1;
10                 char b='\0';
11                 char test[256]={0};
12                 printf("please input the num:\n");
13                 int ret=scanf("%*[\r\n\t]%[^,],%[^,],%d",str,&b,&a);
14                 printf("input is str: %s  b: %c  a: %d And str length is %u, the first character is %u.ret is %d \n",str,b,a,strlen(str),str[0],ret);
15                 char testgets[256];
16                 gets(testgets);
17                 printf("testgets:%s \n",testgets);
18         }
19         return 0;
20 }

在%號中若匹配不到任何字節,則scanf失敗,標准輸入緩沖區中的字段一直保留未被讀取。

 

注:

 %跟*則表示過濾掉滿足條件的字符,%后可跟正則表達式。

此文所有的實驗都是基於下面的程序:
char str[10];
for (int i = 0; i < 10; i++) str[i] = '!';
執行完后str的值為
str = "!!!!!!!!!!"
我們把str的每個字符都初始化為驚嘆號,當str的值發生變化時,使用printf打印str的值,對比先前的驚嘆號,這樣就可以方便的觀察str發生了怎樣的變化。下面我們做幾個小實驗,看看使用sscanf和正則表達式格式化輸入后,str有什么變化。
實驗1:
sscanf( "123456" , "%s" , str) ; ---------str的值為 "123456\0!!!"
這個實驗很簡單,把源字符串"123456"拷貝到str的前6個字符,並且把str的第7個字符設為null字符,也就是\0
實驗2:
sscanf( "123456" , "%3s" , str) ; ---------str的值為 "123\0!!!!!!"
看到沒有,正則表達式的百分號后面多了一個3,這告訴sscanf只拷貝3個字符給str,然后把第4個字符設為null字符。
實驗3:
sscanf( "aaaAAA" , "%[a-z]" , str) ; ---------str的值為 "aaa\0!!!!!!"
從這個實驗開始我們會使用正則表達式,括號里面的a-z就是一個正則表達式,它可以表示從a到z的任意字符,
在繼續討論之前,我們先來看看百分號表示什么意思,%表示選擇 ,%后面的是條件,比如實驗1的"%s",s是一個條件,表示任意字符,"%s"的意思是:只要輸入的東西是一個字符,就把它拷貝給str。實驗2的"%3s"又多了一個條件:只拷貝3個字符。實驗3的“%[a-z]”的條件稍微嚴格一些,輸入的東西不但是字符,還得是一個小寫字母的字符,所以實驗3只拷貝了小寫字母"aaa"給str,別忘了加上null字符。
實驗4:
sscanf( "AAAaaaBBB" , "%[^a-z]" , str) ; ---------str的值為 "AAA\0!!!!!!"
對於所有字符,只要不是小寫字母,都滿足"^a-z"正則表達式,符號^表示邏輯非。前3個字符都不是小寫字符,所以將其拷貝給str,但最后3個字符也不是小寫字母,為什么不拷貝給str呢?這是因為當碰到不滿足條件的字符后,sscanf就會停止執行,不再掃描之后的字符。
實驗5:
sscanf( "AAAaaaBBB" , "%[A-Z]%[a-z]" , str) ; ---------段錯誤
這個實驗的本意是:先把大寫字母拷貝給str,然后把小寫字母拷貝給str,但很不幸,程序運行的時候會發生段錯誤,因為當sscanf掃描到字符a時,違反了條件"%[A-Z]",sscanf就停止執行,不再掃描之后的字符,所以第二個條件也就沒有任何意義,這個實驗說明:不能使用%號兩次或兩次以上
實驗6:

sscanf( "AAAaaaBBB" , "%*[A-Z]%[a-z]" , str) ; ---------str的值為 "aaa\0!!!!!!"
這個實驗出現了一個新的符號:%*,與%相反,%*表示過濾 滿足條件的字符,在這個實驗中,%*[A-Z]過濾了所有大寫字母,然后再使用%[a-z]把之后的小寫字母拷貝給str。如果只有%*,沒有%的話,sscanf不會拷貝任何字符到str,這時sscanf的作用僅僅是過濾字符串。
實驗7:
sscanf( "AAAaaaBBB" , "%[a-z]" , str) ; ---------str的值為 "!!!!!!!!!!"
做完前面幾個實驗后,我們都知道sscanf拷貝完成后,還會在str的后面加上一個null字符,但如果沒有一個字符滿足條件,sscanf不會在str的后面加null字符,str的值依然是10個驚嘆號。這個實驗也說明了,如果不使用%*過濾掉前面不需要的字符,你永遠別想取得中間的字符。
實驗8:
sscanf( "AAAaaaBC=" , "%*[A-Z]%*[a-z]%[^a-z=]" , str) ; ---------str的值為 "BC\0!!!!!!!"
這是一個綜合實驗,但這個實驗的目的不是幫我們復習前面所學的知識,而是展示兩個值得注意的地方:
注意1:%只能使用一次,但%*可以使用多次,比如在這個實驗里面,先用%*[A-Z]過濾大寫字母,然后用%*[a-z]過濾小寫字母。
注意2:^后面可以帶多個條件,且這些條件都受^的作用,比如^a-z=表示^a-z且^=(既不是小寫字母,也不是等於號)。
實驗9:
int k;
sscanf( "AAA123BBB456" , "%*[^0-9]%i" , &k) ; ---------k的值為123
首先,%*[^0-9]過濾前面非數字的字符,然后用%i把數字字符轉換成int型的整數,拷貝到變量k,注意參數必須使用k的地址。
實驗10:
sscanf("123334abcd123", "%[0-9]*", str);
執行完后str的內容是字符串"123334"。[0-9]*是一個非常簡單的正則表達式,意思是匹配數字任意次。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM