【例25.8】使用狀態機將字符數組buf字串中的多餘空格去除並將結果存入數組test中,然後再輸出test中的內容驗證是否符合要求。
【解答】在第一篇第11章例11.5中,給出使用狀態機去除多餘的空格的程序。這裡不是在去除空格過程中打印,而是存入字符數組。打印是輸出到屏幕,只要不輸出多餘的空格即可。但輸出到字符數組則需要保證它的下標在輸入多餘空格時,保持不變。
第1個空格是重要的,狀態要發生改變。這裡把input空格定義為1,其他應為0。
對state而言,關心的是空格,所以字符對應0。第1個空格為1,第2個空格就必須與之區分,定義為2。同理,第3個空格也應為2。對字符不關心,遇到字符(包括標點符號)回到0狀態,只要不是狀態2,就都打印出來。
用j表示下標,用1表示下標變化,0表示不變。
buf \" How are you ? Fine ! thank you.n input 1100010001110000111000001100000100000 state 01200010001220000122000001200000100000 p p pppppppp ppppp ppppppppppppppppp j 01111111111100111111001111111111111111111
根據對應關係,列出如下的狀態跳轉表。
0 0-->0 0 1-->1 1 0-->0 1 1-->2 2 0-->0 2 1-->2
對照上述狀態,可以看出,在原來程序使用
printf (\"%c\" ,c );
輸出的地方,換為語句
test[j]=c ;
即可。而在state=2,input=1並維持state=2的情況下(即2 1-->2)做j--運算,以抵消本輪循環後的j++運算,維持j不變。
下面是在該例程序中修改後的程序和運行結果,為了容易理解,僅僅將原來的printf語句註釋掉而不是刪除。
#include <stdio.h> int get_input (char ); int main () { char buf = \"How are you ? Fine ! thank you.\" ; char test[64] ; // 存入去掉空格後的單詞 int input = 0 , i = 0 , state = 0 ; char c ; int j=0 ; //test 數組下標計數器 while (1 ) { c=buf[i] ; input=get_input (c ); if (c==\'\0\' ) break ; if ((state==0 )&& (input==0 )) { state=0 ; // printf (\"%c\" ,c ); test[j]=c ; } else if ((state==0 )&& (input==1 )) { state=1 ; // printf (\"%c\" ,c ); test[j]=c ; } else if ((state==1 )&& (input==0 )) { state=0 ; // printf (\"%c\" ,c ); test[j]=c ; } else if ((state==1 )&& (input==1 )) { state=2 ; //nothing } else if ((state==2 )&& (input==0 )) { state=0 ; // printf (\"%c\" ,c ); test[j]=c ; } if ((state==2 )&& (input==1 )) { state=2 ; //no out ; j-- ; // 數組不能繼續計數,保持原來的下標 } i++ ; j++ ; } test[j]=\'\0\' ; // 將數組置結束符後輸出 printf (\"%sn\" ,test ); return 0 ; } int get_input (char c ) { if ( c== \' \' ) return 1 ; return 0 ; }
程序運行結果如下:
How are you ? Fine ! thank you.
【25.9】使用函數指針去除多餘空格。
分析一下狀態表:
0 0-->0 0 1-->1 1 0-->0 1 1-->2 2 0-->0 2 1-->2
假設用狀態表示一個數組的下標,則轉移可以作為這個數組元素的值,假設用數組a表示為:a[0][0]=1,a[0][1]=1,a[1][0]=0,a[1][1]=2,a[2][0]=0,a[2][1]=2。
由此可以造一個狀態遷移表state_transition,用來作為下一個狀態的返回,即
state=state_transition[state][input] ; int state_transition[3][2]= { {0 ,1} , {0 ,2} , {0 ,2} } ;
老的state調用函數指針:
pf=act_table[state][input] ; pf ();
再用老的state產生新的state,即
state=state_transition=[state][input] ; // 符合此規律
定義函數指針:
typedef void (*PF )(void );
再定義全局二維指針數組,存6個函數指針,與狀態表一一對應,即對應遷移狀態表所要執行的動作表。
PF act_table[3][2]= { {act_print , act_print} , {act_print , act_null} , {act_print , act_null} } ;
從狀態表找出狀態,從動作表找出動作,這就大大簡化了程序設計。
完整的程序如下。
#include <stdio.h> int get_input (char ); char c ; void act_print (void ) { printf (\"%c\" ,c ); return ; } void act_null (void ) { return ; } // 狀態遷移表,可以作為下一個狀態的返回 int state_transition[3][2]= { {0 ,1} , {0 ,2} , {0 ,2} } ; typedef void (*PF )(void ); // 全局二維指針數組,存6 個函數指針,與狀態表一一對應。 // 對應遷移狀態表所要執行的動作表 PF act_table[3][2]= { {act_print ,act_print} , {act_print ,act_null} , {act_print ,act_null} } ; // 主程序 int main () { char buf=\"How are you ? Fine ! thank you.\" ; int input=0 ,i=0 ,state=0 ; while (1 ) { void (*pf )(void ); // 聲明函數指針 c=buf[i] ; // 如果使用文件,改寫 #if 1 // 如果使用文件刪除此項 input=get_input (c ); if (c==\'\0\' ) break ; #endif #if 0 // 如果使用文件選此項 if (c==EOF ) break ; #endif // 第1 次是老的state ,用它調用函數指針 pf = act_table[state][input] ; pf ( ); // 再用老的state 產生新的state ,供下一次循環使用 state=state_transition[state][input] ; // 符合此規律 i++ ; } printf (\"n\" ); return 0 ; } int get_input (char c ) { if ( c== \' \' ) return 1 ; return 0 ; }
程序運行結果如下:
How are you ? Fine ! thank you.