本文最后更新于 158 天前,其中的信息可能已经有所发展或是发生改变。
预期是想串口发送[CHG]student.name = Hloks,就能完成对应的指令
这里采用的方法是,字段映射表(类似注册表),将变量名与变量实际地址封装,以方便调用
在定义结构体后,定义枚举类型(enum) FieldType,主结构 FieldMapping
typedef enum {
TYPE_STRING,
TYPE_UINT8,
TYPE_FLOAT
...
} FieldType;
typedef struct {
const char *field_name; // 字段名,例如 "student.name"
void *field_ptr; // 地址
FieldType type; // 变量类型
} FieldMapping;
完成后,就可以实例化映射表了
FieldMapping field_table[] = {
{"student.name", &student.name, TYPE_STRING},
{"student.age", &student.age, TYPE_UINT8},
{"student.gpa", &student.gpa, TYPE_FLOAT},
};
#define FIELD_TABLE_SIZE (sizeof(field_table)/sizeof(FieldMapping))
嗯嗯… 看起来可以进行下一步了。在接受数据的函数中,拿到发送的数据 g_usart_rx_buf,加入 parse_and_set_field((char *)g_usart_rx_buf); 然后,完善…
Stop! 再此之前,我们需要加入 g_usart_rx_buf[len] = ‘\0’; 来确认终止符。这是为了确保后续的字符串操作正确识别数据结束位置
接下来,大概整理一下 parse_and_set_field 要实现的功能:
- 遇到非匹配的指令,退出函数
- 移除空格即制表符(如 [CHG]student.name = Hloks 的等号周围的空格)
- 语法检查
- 提取KV
- 匹配字段名并更新数据
移除空格
跟1 一样没什么好说的其实,因为都是char,跳过前5个字符([CHG])然后记得加终止符(方便用户操作)
char cmd_nospace[256]; // 命令字符串的其实位置
size_t j = 0;
for (const char *p = input + 5; *p && j + 1 < sizeof(cmd_nospace); p++) {
if (*p != ' ' && *p != '\t') {
cmd_nospace[j++] = *p;
}
}
cmd_nospace[j] = '\0';
提取KV值
再此之前,我们先得到等号的位置 const char *eq = strchr(cmd_nospace, ‘=’);
现在,可以定位 Key 的位置了
char field[64] = {0};
size_t field_len = eq - cmd_nospace;
if (field_len == 0 || field_len >= sizeof(field)) {
printf("[ERR] Invalid field name length\r\n");
return;
}
strncpy(field, cmd_nospace, field_len);
field[field_len] = '\0';
同理,定位 Value,这里的换行符是 CRLF 注意
// 提取字段值
const char *val_start = eq + 1;
const char *val_end = strpbrk(val_start, "\"\r\n");
char temp[64] = {0};
size_t val_len = val_end ? (size_t)(val_end - val_start) : strlen(val_start);
if (val_len >= sizeof(temp)) val_len = sizeof(temp) - 1;
strncpy(temp, val_start, val_len);
temp[val_len] = '\0';