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.
  

《C语言解惑》