讀古今文學網 > C語言解惑 > 25.7 使用狀態機設計程序 >

25.7 使用狀態機設計程序

【例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.