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