有没有比使用sscanf更好的方法来实现此结果?

时间:2019-01-07 16:58:16

标签: c parsing scanf

我需要扫描来自串行流的各种传入消息,以检查它们是否包含此字符串:

“一切:已收到:switchX yy(y)”

其中X = 1到9,并且yy(y)为“ on”或“ off”。即“一切:已接收:将switch4打开”或“一切:已接收:将switch2关闭”等

我正在ATMega328上使用以下代码进行检查,并将相关变量传递给send()函数:

valid_data = sscanf(serial_buffer, "Everything: Received: switch%u %s", &switch_number, command);
if(valid_data == 2)
{
  if(strcmp(command, "on") == 0)
    {
       transmit(switch_number, 1);
    }
    if(strcmp(command, "off") == 0)
    {
       transmit(switch_number, 0);
    }
}

当serial_buffer输入ISR检测到“ \ n”时,将触发检查。 '\ 0'附加到串行流以终止字符串。

它可以工作,但我不追求空间/处理能力,但我只是想知道这是否是实现所需结果的最佳方法?

1 个答案:

答案 0 :(得分:2)

  

它可以正常工作,但我并没有追求空间/处理能力,但我只是想知道这是否是实现所需结果的最佳方法?

目前尚不清楚您要我们判断的标准,因为速度和内存使用都不是紧迫的问题,但是在没有这些考虑的压力下,我个人将代码的简单性和清晰度视为源代码的最重要标准,当然,除了正确性之外。

从这个角度来看,基于sscanf()的解决方案是很好的,尤其是使用像您这样的相对简单的格式字符串时。您要匹配的行的模式在格式字符串中非常清楚,并且以下逻辑也很简单明了。另外,它还应该产生少量代码,因为库函数可以完成大部分工作,并且可以合理地希望实现已为优化该函数付出了一定的努力以取得良好的性能,因此即使在那些方面,它也可能是一个胜利。您不太担心的标准。

但是,存在一些可能的正确性问题:

  • sscanf()与字面上的空白不符。格式字符串中的一个或多个空格字符的运行与输入中任何零个或多个空格字符的运行相匹配。
  • sscanf()在大多数字段之前,尤其是在%u字段之前,跳过前导空白。
  • 可以扫描超出指定范围1-9的开关号。
  • 命令缓冲区很容易被溢出。
  • sscanf()将在最后一个字段匹配后忽略输入字符串中的任何内容

如果需要,可以解决所有这些问题。例如,这里是一种处理所有单词的替代方法,除了单词之间的空白量(但包括避免在“ switch”和数字之间的空白):

unsigned char switch_number;
int nchars = 0;

int valid_data = sscanf(serial_buffer, "Everything: Received: switch%c %n",
        &switch_number, &nchars);

if (valid_data >= 1 && switch_number - (unsigned) '1' < 9) {
    char *command = serial_buffer + nchars;

    if (strcmp(command, "on") == 0) {
        transmit(switch_number - '0', 1);
    } else if (strcmp(command, "off") == 0) {
        transmit(switch_number - '0', 0);
    }  // else not a match
} // else not a match

与您的主要区别包括

  • 通过%c指令读取开关号,该指令读取单个字符而不会跳过前导空格。验证条件switch_number - (unsigned) '1' < 9确保读取的字符在'1'和'9'之间。它利用了无符号算术回绕的事实。

  • 不是将命令读入单独的缓冲区中,而是通过%n指令捕获前导子字符串的长度。这样可以测试整个尾部是否处于“打开”和“关闭”状态,从而消除了对额外缓冲区的需要,并使您能够拒绝带有尾随单词的行。

  • 如果您也想检查所有空格是否完全匹配,那么%n也可以提供帮助。例如,

    if (nchars == 30 && serial_buffer[11] == ' ' && serial_buffer[21] == ' '
            serial_buffer[29] == ' ') // it's OK