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

有没有比使用sscanf更好的方法来实现这一结果?,c,parsing,scanf,C,Parsing,Scanf,我需要扫描来自串行流的各种传入消息,以检查它们是否包含以下字符串: “所有内容:已接收:开关X yy(y)” 其中X=1至9,且yy(y)为“开”或“关”。i、 e.“接收到的所有信息:打开4”或“接收到的所有信息:关闭2”等 我在ATMega328上使用以下代码进行检查,并将相关变量传递给transmit()函数: valid_data = sscanf(serial_buffer, "Everything: Received: switch%u %s", &switch_number

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

“所有内容:已接收:开关X yy(y)”

其中X=1至9,且yy(y)为“开”或“关”。i、 e.“接收到的所有信息:打开4”或“接收到的所有信息:关闭2”等

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

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);
    }
}
当串行缓冲区输入ISR检测到“\n”时,将触发检查\将0'附加到串行流以终止字符串

它是有效的,我不需要空间/处理能力,但我只是想知道这是否是实现所需结果的最佳方式

它是有效的,我不需要空间/处理能力,但我只是想知道这是否是实现所需结果的最佳方式

不清楚您希望我们根据哪些标准来判断,因为速度和内存使用都不是一个迫切的问题,但是在没有这些考虑的压力下,我个人认为代码的简单性和清晰性是源代码最重要的标准,当然,除了正确性之外

从这个角度来看,基于
sscanf()
的解决方案是好的,尤其是使用一个相对简单的格式字符串,比如您实际上拥有的格式字符串。要匹配的行的模式在格式字符串中非常清楚,下面的逻辑也非常清楚和简单。作为奖励,它还应该生成小代码,因为库函数完成了大部分工作,并且有理由希望实现在优化该函数以获得良好性能方面付出了一些努力,因此即使在那些您不太关心的标准上,它也可能是一个胜利

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

  • sscanf()
    与空白不匹配。格式字符串中一个或多个空白字符的运行与输入中任何零个或多个空白字符的运行相匹配
  • sscanf()
    跳过大多数字段前面的前导空格,尤其是在
    %u
    字段前面
  • 可以扫描指定范围1-9之外的开关编号
  • 命令缓冲区很容易溢出
  • sscanf()
    将在最后一个字段匹配后忽略输入字符串中的任何内容
如果需要,所有这些问题都可以解决。例如,这里有一个替代方案,可以处理除单词之间的空白量以外的所有字符(但包括“开关”和数字之间的空白):


如果需要,您可以编写一个通用解析器,或者使用来找到正确的偏移量开始读取。“最佳”方法是通过什么度量?讨论各种方法的优缺点是有趣和有意义的,但对于SO的问答形式来说并不太合适。一般来说,我很少建议对任何东西使用
scanf
,因为它在检测到的分隔符和其他类似的东西方面定义得相当糟糕,而倾向于选择
fgets
并更手动地解析结果字符串,但是如果你可以不使用它,使用
scanf
更为紧凑。
sscanf
是适合此项工作的工具。您可以通过在字符串末尾添加
\n
来强制执行更严格的输入格式,并通过添加
%4s
来提高安全性,以确保最后一个令牌(命令)不会溢出您的输入缓冲区(这是4个字符,因为
NUL
字符存储在字符串末尾,由
sscanf
自动追加)。旁白:如果您的第一个条件匹配,请使用
else
以避免测试另一个条件。对于此类解析问题,如果字符串与格式不匹配,请提供所需结果-或者您是否建议字符串始终格式良好?非常有用。感谢您的回复。
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
if (nchars == 30 && serial_buffer[11] == ' ' && serial_buffer[21] == ' '
        serial_buffer[29] == ' ') // it's OK