搜索
bottom↓
回复: 21

请教一个能从串口通讯中检出特殊字符串的方法

[复制链接]

出0入4汤圆

发表于 2019-1-31 13:56:23 | 显示全部楼层 |阅读模式
本帖最后由 liuqian 于 2019-1-31 14:00 编辑

现在搞一个通讯协议,里面包含一些特殊的字符串,需要把这些字符串捡出来,比如
数据段1 : 1234567ERROR8474674
数据段2 : 37649944SYSTEMHALT4866289
这里面ERROR和SYSTEMHALT是需要捡出来的
数据流要把匹配字符串去掉,然后转发出去
数据段1变成12345678474674
数据段2变成376499444866289
假设正常的数据流中不会出现ERROR和SYSTEMHALT这样的字符串,但协议本身是未知的,就是说每收到一个字符,就要做匹配,如果能匹配,就先缓冲下来,不转发;不匹配的要马上转发出去。


  1. const char ERROR[]="ERROR";
  2. const char WARN[]="WARN";
  3. const char HALT[]="SYSTEMHALT";

  4. const struct {char * str; uint8_t len;} MATCHED_STR[3]={
  5. {ERROR,5},
  6. {HALT,10},
  7. {WARN,4}
  8. };
  9. // return:
  10. //                0 - n : 和一个字符串完全匹配了
  11. //                -1 :  部分匹配了字符串,数据被缓冲
  12. //                -2 : 发生了不匹配,需要处理缓冲区数据
  13. int MatchByte()
  14. {
  15.         if(matchedLen<MATCHED_BUF_SIZE)
  16.         {
  17.                 matchedBuf[matchedLen++]=getch();
  18.         }
  19.         int i;
  20.         for(i=0;i<3;i++)
  21.         {
  22.                 if(memcmp(matchedBuf,MATCHED_STR[i].str,matchedLen)==0) //matched first matchedLen bytes
  23.                 {
  24.                         if(matchedLen==MATCHED_STR[i].len)
  25.                         {// all matched
  26.                                 return i;
  27.                         }
  28.                         else
  29.                         {// partly matched
  30.                                 return PARTLY_MATCHED;
  31.                         }
  32.                 }
  33.         }
  34.         // none matched
  35.         // 这里还没有想清楚怎么处理,卡住了
  36.         return NONE_MATCHED;
  37. }
复制代码


调用

  1. if(isDataRcvd())
  2. {
  3.         switch(MatchByte())
  4.         {
  5.                 case 0:
  6.                         error_handler();
  7.                 break;
  8.                 case 1:
  9.                         halt_handler();
  10.                 break;
  11.                 case 2:
  12.                         warn_handler();
  13.                 break;
  14.                 case NONE_MATCHED:
  15.                         // 这里还没有想清楚怎么处理,卡住了
  16.                 break;
  17.                 case PARTLY_MATCHED:
  18.                         // do nothing
  19.                 break;
  20.         }
  21. }
复制代码

阿莫论坛20周年了!感谢大家的支持与爱护!!

月入3000的是反美的。收入3万是亲美的。收入30万是移民美国的。收入300万是取得绿卡后回国,教唆那些3000来反美的!

出0入162汤圆

发表于 2019-1-31 14:00:58 来自手机 | 显示全部楼层
不能先缓存下来再查找?

出0入0汤圆

发表于 2019-1-31 14:14:14 | 显示全部楼层
看看库函数 strstr 行不行

出0入0汤圆

发表于 2019-1-31 14:24:17 | 显示全部楼层
你这个字符流又没有停止位,又没有传输间隔的说明,但是我觉得自己写函数处理吧,

出0入162汤圆

发表于 2019-1-31 14:44:59 | 显示全部楼层
本帖最后由 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[16];
      switch(State)
        {
          case 0:
                    if(RecData=='E')
                     { State=1;
                       Index=1;
                       Buf[0]=RecData;
                       break;
                        }
                    if(RecData=='S')
                     { State=2;
                       Index=1
                      Buf[0]=RecData;
                       break;
                        }
                     if(RecData=='W')
                     { State=3;
                       Index=1
                       Buf[0]=RecData;
                       break;
                       }
                    SendData(&RecData,1);//把数据发送出去 1个字节
                    break;
        case 1:  //收到'E'
                  if((RecData!=ERROR[Index])
                    {
                      SendData(Buf,Index);//把缓存的数据发送出去
                       State=0;//回到初始状态
                       break;
                       }
                   Buf[Index]=RecData;
                  Index++;
                 if(Index==strlen(ERROR))
                   State=0;  //完整接收了也回到初始状态
                 break;
       case 2:  //收到'S'
                  if((RecData!=HALT[Index])
                    {
                        SendData(Buf,Index);//把缓存的数据发送出去
                       State=0;//回到初始状态
                       break;
                       }
                  Buf[Index]=RecData;
                  Index++;
                 if(Index==strlen(HALT))
                   State=0;  //完整接收了也回到初始状态
                 break;
         case 3:  //收到'W'
                  if((RecData!=WARN[Index])
                    {
                       SendData(Buf,Index);//把缓存的数据发送出去
                       State=0;//回到初始状态
                       break;
                       }
                   Buf[Index]=RecData;
                  Index++;
                 if(Index==strlen(WARN))
                   State=0;  //完整接收了也回到初始状态
                 break;
          default:
                      State=0;
                     break;
         }

出0入0汤圆

发表于 2019-1-31 17:31:41 | 显示全部楼层
状态机。。。

出0入0汤圆

发表于 2019-2-15 12:00:21 | 显示全部楼层
AWEN2000 发表于 2019-1-31 14:44
RecData 表示当前接收到的1个字节数据
const char ERROR[]="ERROR";
const char WARN[]="WARN";

虽然我也用状态机,但这里似乎不太好处理。
比如收到的是“EERROR”,则在状态1时认为不匹配,就会漏掉“ERROR”。
也许需要做一个FIFO,缓冲最后5个字节,每收到一个字节都让这个FIFO与“ERROR”去比较,只是比较占CPU。

出0入0汤圆

发表于 2019-2-15 12:48:54 | 显示全部楼层
每收到一个字符处理一次,用状态机
假设里面包含EERROR,第一个E进入下一状态,第二个E不匹配ERROR,是PASS第一个E,而不是第二个E也PASS,第二个E要重新过一遍状态机

出0入0汤圆

发表于 2019-2-15 12:55:04 | 显示全部楼层
当然像这种匹配很多种字符串的话,用状态机比较费劲,编程方便的话就是FIFO,每收到一个字节比一次,比较其实不费时间,把FIFO效率提高就行

出0入162汤圆

发表于 2019-2-15 13:06:01 来自手机 | 显示全部楼层
RecData 表示当前接收到的1个字节数据 const char ERROR[]="ERROR"; const char WARN[]="WARN"; const char HALT[]="SYSTEMHALT";  static INT8U State; static INT8U Index; static INT8U Buf[16]; INT8U Flag; Start:       Flag=0;       switch(State)         {           case 0:                     if(RecData=='E')                      { State=1;                        Index=1;                        Buf[0]=RecData;                        break;                         }                     if(RecData=='S')                      { State=2;                        Index=1                       Buf[0]=RecData;                        break;                         }                      if(RecData=='W')                      { State=3;                        Index=1                        Buf[0]=RecData;                        break;                        }                     SendData(&RecData,1);//把数据发送出去 1个字节                     break;          case 1:  //收到'E'                   if((RecData!=ERROR[Index])                     {                       SendData(Buf,Index);//把缓存的数据发送出去                        State=0;//回到初始状态                        Flag=1;再次检测"E"                        break;                        }                    Buf[Index]=RecData;                   Index++;                  if(Index==strlen(ERROR))                    State=0;  //完整接收了也回到初始状态                  break;        case 2:  //收到'S'                   if((RecData!=HALT[Index])                     {                         SendData(Buf,Index);//把缓存的数据发送出去                        State=0;//回到初始状态                       Flag=1;                        break;                        }                   Buf[Index]=RecData;                   Index++;                  if(Index==strlen(HALT))                    State=0;  //完整接收了也回到初始状态                  break;          case 3:  //收到'W'                   if((RecData!=WARN[Index])                     {                        SendData(Buf,Index);//把缓存的数据发送出去                        State=0;//回到初始状态                        Flag=1;                        break;                        }                    Buf[Index]=RecData;                   Index++;                  if(Index==strlen(WARN))                    State=0;  //完整接收了也回到初始状态                  break;           default:                       State=0;                      break;          } if(Flag==1)     goto Start;

出0入162汤圆

发表于 2019-2-15 13:08:04 来自手机 | 显示全部楼层
RecData 表示当前接收到的1个字节数据
const char ERROR[]="ERROR";
const char WARN[]="WARN";
const char HALT[]="SYSTEMHALT";

static INT8U State;
static INT8U Index;
static INT8U Buf[16];
INT8U Flag;
Start:
      Flag=0;
      switch(State)
        {
          case 0:
                    if(RecData=='E')
                     { State=1;
                       Index=1;
                       Buf[0]=RecData;
                       break;
                        }
                    if(RecData=='S')
                     { State=2;
                       Index=1
                      Buf[0]=RecData;
                       break;
                        }
                     if(RecData=='W')
                     { State=3;
                       Index=1
                       Buf[0]=RecData;
                       break;
                       }
                    SendData(&RecData,1);//把数据发送出去 1个字节
                    break;
        case 1:  //收到'E'
                  if((RecData!=ERROR[Index])
                    {
                      SendData(Buf,Index);//把缓存的数据发送出去
                       State=0;//回到初始状态
                       Flag=1;再次检测"E"
                       break;
                       }
                   Buf[Index]=RecData;
                  Index++;
                 if(Index==strlen(ERROR))
                   State=0;  //完整接收了也回到初始状态
                 break;
       case 2:  //收到'S'
                  if((RecData!=HALT[Index])
                    {
                        SendData(Buf,Index);//把缓存的数据发送出去
                       State=0;//回到初始状态
                      Flag=1;
                       break;
                       }
                  Buf[Index]=RecData;
                  Index++;
                 if(Index==strlen(HALT))
                   State=0;  //完整接收了也回到初始状态
                 break;
         case 3:  //收到'W'
                  if((RecData!=WARN[Index])
                    {
                       SendData(Buf,Index);//把缓存的数据发送出去
                       State=0;//回到初始状态
                       Flag=1;
                       break;
                       }
                   Buf[Index]=RecData;
                  Index++;
                 if(Index==strlen(WARN))
                   State=0;  //完整接收了也回到初始状态
                 break;
          default:
                      State=0;
                     break;
         }
if(Flag==1)
    goto Start;




状态1里面若出现不匹配,把以前缓存的数据发出去。同时对当前的字节再次检测特征字头

出0入8汤圆

发表于 2019-2-15 13:42:43 | 显示全部楼层
yerrmin 发表于 2019-2-15 12:48
每收到一个字符处理一次,用状态机
假设里面包含EERROR,第一个E进入下一状态,第二个E不匹配ERROR,是PASS ...

用KMP算法,不需要再过状态机

出0入8汤圆

发表于 2019-2-15 13:45:03 | 显示全部楼层
如果是我做的话,我会用词法分析工具自动成生成状态机

串口数据不停止往状态机里写,状态机自动吐数据

出0入0汤圆

发表于 2019-2-16 10:06:32 | 显示全部楼层
本帖最后由 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算法似乎不错,相比挨个位置匹配,效率提高了,但是程序较复杂。

出0入0汤圆

发表于 2019-2-16 11:41:33 | 显示全部楼层
lcw_swust 发表于 2019-2-16 10:06
这种方法的确可以适用大多数情况,但在某些特殊情况下仍会漏掉。
比如在“SYSYST”中查找“SYST”。
只是 ...

kmp算法需要先把数都收下来才能查的,似乎不能直接用?除非收一部分先查这一部分。

出0入0汤圆

发表于 2019-2-16 11:51:02 | 显示全部楼层
bangbangji 发表于 2019-2-16 11:41
kmp算法需要先把数都收下来才能查的,似乎不能直接用?除非收一部分先查这一部分。 ...

是啊,要缓冲一部分。

出0入0汤圆

发表于 2019-2-16 11:59:37 | 显示全部楼层
这需求和GPRS模块AT命令解析类似。

出0入4汤圆

 楼主| 发表于 2019-2-17 16:14:14 | 显示全部楼层
现在用FIFO缓冲,然后再加一个超时,每收一个字符就比较。效率比较低,但功能实现了。有空把程序整一下再贴出来。

出0入8汤圆

发表于 2019-2-17 21:21:25 来自手机 | 显示全部楼层
bangbangji 发表于 2019-2-16 11:41
kmp算法需要先把数都收下来才能查的,似乎不能直接用?除非收一部分先查这一部分。 ...

kmp算法是先对特征字符串进行计算
计算出匹配错误时,下一次需要从哪里重新开始匹配
接受还是一字节一字节的处理,不需要全部接收再处理

出0入8汤圆

发表于 2019-2-17 21:26:20 来自手机 | 显示全部楼层
lcw_swust 发表于 2019-2-16 10:06
这种方法的确可以适用大多数情况,但在某些特殊情况下仍会漏掉。
比如在“SYSYST”中查找“SYST”。
只是 ...

正则表达式/词法分析的处理方式是效率最高的,也是最灵活的
对于sysyst中匹配syst的情况,很容易处理
代码也是最复杂的,不过一般这样的代码都不用手写
有工具生成

出0入4汤圆

发表于 2019-2-17 23:38:37 | 显示全部楼层
anjiyifan 发表于 2019-2-16 11:59
这需求和GPRS模块AT命令解析类似。

AT命令解析的场景,允许先把收到的数据全部缓存起来,这样就容易多了。而且AT指令要求的响应不高,逐个匹配都可以。

出0入4汤圆

发表于 2019-2-18 00:06:15 | 显示全部楼层
不错的流串匹配算法,也许会用到,谢谢
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-24 21:16

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表