如果在读取末尾的双回车 (CR) 之前不处理帧类型,然后在双 CR 之后插入空字节,则可以使用sscanf()
或其他解析技术提取帧类型信息。
char frame_type[32];
char colon[2];
int offset;
if (sscanf(ring_buffer, ":\\%31[^:]%[:]%n", frame_type, colon, &offset) != 2)
…deal with malformatted frame…
现在您将帧类型frame_type
作为字符串输入。稍微奇数%[:]
只匹配一个冒号;但是,关键是它被视为成功的转换,并且%n
转换将是有效的。如果改为:
if (sscanf(":\\%31[^:]:%n", frame_type, &offset) != 1)
…deal with malformatted frame…
您将不知道尾随的冒号是否匹配,并且您将不知道是否offset
包含有效值(找到前一个字符冒号的字符串的偏移量)。
您至少有两种框架类型;总共有多少(目前,将来可能有)?在某一层面上,这并不重要。您可以针对可能的帧类型使用一系列字符串比较,或者您可以使用帧类型字符串的散列并将其与一组有效散列值进行比较,或者您可以设计另一种机制。
一旦您知道您拥有哪种帧类型,您就知道使用什么格式字符串来读取其余数据。由于您阅读了双 CR,因此您知道结尾包含该内容 - 您无需再次验证它。
例如,对于传感器框架,您可以使用:
if (sscanf(ring_buffer + offset, "%.2f;%.2f;%.2f;%d;%d;%u",
&input.temperature, &input.current, &input.voltage,
&input.dutycycle, &input.lightsensor, &input.message) != 6)
…deal with malformatted sensor frame…
或者,对于命令框架,您可以使用:
if (sscanf(ring_buffer + offset, "%u;%.5f\r\r", &input.command, &input.data) != 2)
…deal with malformatted command frame…
唯一复杂的因素是您使用的是环形缓冲区。这可能意味着您的传感器帧被拆分,以便前 5 个字节位于环形缓冲区的末尾,其余字节位于缓冲区的开头。坦率地说,如果你能负担得起空间和复制,将环形缓冲区转换为常规(线性?)缓冲区将是最简单的。如果这绝对不是一种选择,那么您可能会被完全不使用sscanf()
的困扰;您将需要编写自己的变体sscanf()
,可以告知环形缓冲区的形状并使用它,或者您必须一次处理角色。
也许您的自定义功能是:
int rbscanf(const char *rb_base, int rb_len, int rb_off, const char *format, ...);
环形缓冲区开始于rb_base
并且rb_len
总共是字节长;数据开始于&rb_base[rb_off]
。您可能需要指定缓冲区长度和数据长度(rb_len
和rb_nbytes
)。您可能已经有一个描述环形缓冲区的结构,在这种情况下,您可以将其传递(指向)该函数。
或者,如果您在读取整个帧之前处理数据,那么您可以在读取字节时进行验证。您仍然需要累积字符串和数字以进行转换。您可能会使用strtol()
andstrtod()
而不是atoi()
and atof()
; 您将需要了解错误,包括未转换的尾随字符,而atoi()
和atof()
函数无法告诉您。这些功能需要小心strtoX()
,但它们是有效的。