请教一个能从串口通讯中检出特殊字符串的方法
本帖最后由 liuqian 于 2019-1-31 14:00 编辑现在搞一个通讯协议,里面包含一些特殊的字符串,需要把这些字符串捡出来,比如
数据段1 : 1234567ERROR8474674
数据段2 : 37649944SYSTEMHALT4866289
这里面ERROR和SYSTEMHALT是需要捡出来的
数据流要把匹配字符串去掉,然后转发出去
数据段1变成12345678474674
数据段2变成376499444866289
假设正常的数据流中不会出现ERROR和SYSTEMHALT这样的字符串,但协议本身是未知的,就是说每收到一个字符,就要做匹配,如果能匹配,就先缓冲下来,不转发;不匹配的要马上转发出去。
const char ERROR[]="ERROR";
const char WARN[]="WARN";
const char HALT[]="SYSTEMHALT";
const struct {char * str; uint8_t len;} MATCHED_STR={
{ERROR,5},
{HALT,10},
{WARN,4}
};
// return:
// 0 - n : 和一个字符串完全匹配了
// -1 :部分匹配了字符串,数据被缓冲
// -2 : 发生了不匹配,需要处理缓冲区数据
int MatchByte()
{
if(matchedLen<MATCHED_BUF_SIZE)
{
matchedBuf=getch();
}
int i;
for(i=0;i<3;i++)
{
if(memcmp(matchedBuf,MATCHED_STR.str,matchedLen)==0) //matched first matchedLen bytes
{
if(matchedLen==MATCHED_STR.len)
{// all matched
return i;
}
else
{// partly matched
return PARTLY_MATCHED;
}
}
}
// none matched
// 这里还没有想清楚怎么处理,卡住了
return NONE_MATCHED;
}
调用
if(isDataRcvd())
{
switch(MatchByte())
{
case 0:
error_handler();
break;
case 1:
halt_handler();
break;
case 2:
warn_handler();
break;
case NONE_MATCHED:
// 这里还没有想清楚怎么处理,卡住了
break;
case PARTLY_MATCHED:
// do nothing
break;
}
}
不能先缓存下来再查找? 看看库函数 strstr 行不行 你这个字符流又没有停止位,又没有传输间隔的说明,但是我觉得自己写函数处理吧, 本帖最后由 AWEN2000 于 2019-1-31 14:52 编辑
RecData 表示当前接收到的1个字节数据
const char ERROR[]="ERROR";
const char WARN[]="WARN";
const char HALT[]="SYSTEMHALT";
static INT8U State;
static INT8U Index;
static INT8U Buf;
switch(State)
{
case 0:
if(RecData=='E')
{ State=1;
Index=1;
Buf=RecData;
break;
}
if(RecData=='S')
{ State=2;
Index=1
Buf=RecData;
break;
}
if(RecData=='W')
{ State=3;
Index=1
Buf=RecData;
break;
}
SendData(&RecData,1);//把数据发送出去 1个字节
break;
case 1://收到'E'
if((RecData!=ERROR)
{
SendData(Buf,Index);//把缓存的数据发送出去
State=0;//回到初始状态
break;
}
Buf=RecData;
Index++;
if(Index==strlen(ERROR))
State=0;//完整接收了也回到初始状态
break;
case 2://收到'S'
if((RecData!=HALT)
{
SendData(Buf,Index);//把缓存的数据发送出去
State=0;//回到初始状态
break;
}
Buf=RecData;
Index++;
if(Index==strlen(HALT))
State=0;//完整接收了也回到初始状态
break;
case 3://收到'W'
if((RecData!=WARN)
{
SendData(Buf,Index);//把缓存的数据发送出去
State=0;//回到初始状态
break;
}
Buf=RecData;
Index++;
if(Index==strlen(WARN))
State=0;//完整接收了也回到初始状态
break;
default:
State=0;
break;
} 状态机。。。 AWEN2000 发表于 2019-1-31 14:44
RecData 表示当前接收到的1个字节数据
const char ERROR[]="ERROR";
const char WARN[]="WARN";
虽然我也用状态机,但这里似乎不太好处理。
比如收到的是“EERROR”,则在状态1时认为不匹配,就会漏掉“ERROR”。
也许需要做一个FIFO,缓冲最后5个字节,每收到一个字节都让这个FIFO与“ERROR”去比较,只是比较占CPU。 每收到一个字符处理一次,用状态机
假设里面包含EERROR,第一个E进入下一状态,第二个E不匹配ERROR,是PASS第一个E,而不是第二个E也PASS,第二个E要重新过一遍状态机 当然像这种匹配很多种字符串的话,用状态机比较费劲,编程方便的话就是FIFO,每收到一个字节比一次,比较其实不费时间,把FIFO效率提高就行 RecData 表示当前接收到的1个字节数据 const char ERROR[]="ERROR"; const char WARN[]="WARN"; const char HALT[]="SYSTEMHALT";static INT8U State; static INT8U Index; static INT8U Buf; INT8U Flag; Start: Flag=0; switch(State) { case 0: if(RecData=='E') { State=1; Index=1; Buf=RecData; break; } if(RecData=='S') { State=2; Index=1 Buf=RecData; break; } if(RecData=='W') { State=3; Index=1 Buf=RecData; break; } SendData(&RecData,1);//把数据发送出去 1个字节 break; case 1: //收到'E' if((RecData!=ERROR) { SendData(Buf,Index);//把缓存的数据发送出去 State=0;//回到初始状态 Flag=1;再次检测"E" break; } Buf=RecData; Index++; if(Index==strlen(ERROR)) State=0; //完整接收了也回到初始状态 break; case 2: //收到'S' if((RecData!=HALT) { SendData(Buf,Index);//把缓存的数据发送出去 State=0;//回到初始状态 Flag=1; break; } Buf=RecData; Index++; if(Index==strlen(HALT)) State=0; //完整接收了也回到初始状态 break; case 3: //收到'W' if((RecData!=WARN) { SendData(Buf,Index);//把缓存的数据发送出去 State=0;//回到初始状态 Flag=1; break; } Buf=RecData; Index++; if(Index==strlen(WARN)) State=0; //完整接收了也回到初始状态 break; default: State=0; break; } if(Flag==1) goto Start; RecData 表示当前接收到的1个字节数据
const char ERROR[]="ERROR";
const char WARN[]="WARN";
const char HALT[]="SYSTEMHALT";
static INT8U State;
static INT8U Index;
static INT8U Buf;
INT8U Flag;
Start:
Flag=0;
switch(State)
{
case 0:
if(RecData=='E')
{ State=1;
Index=1;
Buf=RecData;
break;
}
if(RecData=='S')
{ State=2;
Index=1
Buf=RecData;
break;
}
if(RecData=='W')
{ State=3;
Index=1
Buf=RecData;
break;
}
SendData(&RecData,1);//把数据发送出去 1个字节
break;
case 1: //收到'E'
if((RecData!=ERROR)
{
SendData(Buf,Index);//把缓存的数据发送出去
State=0;//回到初始状态
Flag=1;再次检测"E"
break;
}
Buf=RecData;
Index++;
if(Index==strlen(ERROR))
State=0; //完整接收了也回到初始状态
break;
case 2: //收到'S'
if((RecData!=HALT)
{
SendData(Buf,Index);//把缓存的数据发送出去
State=0;//回到初始状态
Flag=1;
break;
}
Buf=RecData;
Index++;
if(Index==strlen(HALT))
State=0; //完整接收了也回到初始状态
break;
case 3: //收到'W'
if((RecData!=WARN)
{
SendData(Buf,Index);//把缓存的数据发送出去
State=0;//回到初始状态
Flag=1;
break;
}
Buf=RecData;
Index++;
if(Index==strlen(WARN))
State=0; //完整接收了也回到初始状态
break;
default:
State=0;
break;
}
if(Flag==1)
goto Start;
状态1里面若出现不匹配,把以前缓存的数据发出去。同时对当前的字节再次检测特征字头 yerrmin 发表于 2019-2-15 12:48
每收到一个字符处理一次,用状态机
假设里面包含EERROR,第一个E进入下一状态,第二个E不匹配ERROR,是PASS ...
用KMP算法,不需要再过状态机 如果是我做的话,我会用词法分析工具自动成生成状态机
串口数据不停止往状态机里写,状态机自动吐数据 本帖最后由 lcw_swust 于 2019-2-16 10:12 编辑
AWEN2000 发表于 2019-2-15 13:08
RecData 表示当前接收到的1个字节数据
const char ERROR[]="ERROR";
const char WARN[]="WARN";
这种方法的确可以适用大多数情况,但在某些特殊情况下仍会漏掉。
比如在“SYSYST”中查找“SYST”。
只是这种情况相当少,一般情况够用了。
12楼说的KMP算法似乎不错,相比挨个位置匹配,效率提高了,但是程序较复杂。 lcw_swust 发表于 2019-2-16 10:06
这种方法的确可以适用大多数情况,但在某些特殊情况下仍会漏掉。
比如在“SYSYST”中查找“SYST”。
只是 ...
kmp算法需要先把数都收下来才能查的,似乎不能直接用?除非收一部分先查这一部分。 bangbangji 发表于 2019-2-16 11:41
kmp算法需要先把数都收下来才能查的,似乎不能直接用?除非收一部分先查这一部分。 ...
是啊,要缓冲一部分。 这需求和GPRS模块AT命令解析类似。 现在用FIFO缓冲,然后再加一个超时,每收一个字符就比较。效率比较低,但功能实现了。有空把程序整一下再贴出来。 bangbangji 发表于 2019-2-16 11:41
kmp算法需要先把数都收下来才能查的,似乎不能直接用?除非收一部分先查这一部分。 ...
kmp算法是先对特征字符串进行计算
计算出匹配错误时,下一次需要从哪里重新开始匹配
接受还是一字节一字节的处理,不需要全部接收再处理 lcw_swust 发表于 2019-2-16 10:06
这种方法的确可以适用大多数情况,但在某些特殊情况下仍会漏掉。
比如在“SYSYST”中查找“SYST”。
只是 ...
正则表达式/词法分析的处理方式是效率最高的,也是最灵活的
对于sysyst中匹配syst的情况,很容易处理
代码也是最复杂的,不过一般这样的代码都不用手写
有工具生成 anjiyifan 发表于 2019-2-16 11:59
这需求和GPRS模块AT命令解析类似。
AT命令解析的场景,允许先把收到的数据全部缓存起来,这样就容易多了。而且AT指令要求的响应不高,逐个匹配都可以。 不错的流串匹配算法,也许会用到,谢谢
页:
[1]