嵌入式基础--解析字符串之模式匹配

接上一篇文章嵌入式基础--解析字符串(1),笔者给大家介绍一个更加简单的解析工具,那就是sscanf。

热身

大家在上C语言课,做C语言课程设计或实验时,应该经常接触printf和scanf,前者打印字符中到标准输出,而后者从标准输入读取并解析字符串。

sscanf和scanf类似,只不过它并不从标准输入读取,而是直接解析用户传入的字符串。

int sscanf(const char *str, const char *format, ...);
  • str 待解析的字符串
  • format 格式化参数
  • ... 变长参数,为一系列用于存放解析结果的变量的地址
  • 返回成功解析的字段数量

格式化参数是啥意思?我们先来热身下。

static void test(void)
{
 const char *str = "Today is 2021.7.31";

 int year = 0;
 int month = 0;
 int day = 0;
 int ret;

 ret = sscanf(str, "Today is %d.%d.%d", &year, &month, &day);
 printf("ret=%d, year:%d, month:%d, day:%d\n", ret, year, month, day);
}

"Today is %d.%d.%d"为格式化参数,里面有普通字符,如Today is,和格式说明符%d表int类型。sscanf依据格式化参数来解析str。对于普通字符,sscanf检查str是否与其一致。对于格式说明符,则按其含义来提取str中的内容,并将结果存入地址参数中。上述代码演示了提取年月日信息的方法,结果如下:

ret=3, year:2021, month:7, day:31

如果还是对格式化参数没有印象的话,同学,你肯定没认真上C语言课,要不翻书复习下呗。笔者今天不是想从零开始讲sscanf,而是介绍一个鲜为人知的用法。

问题

再看一个例子,解析域名和端口号。

static void test2(void)
{
 const char *str = "www.baidu.com:80";

 char addr[64] = "";
 int port = 0;
 int ret;

 ret = sscanf(str, "%s:%d", addr, &port);
 printf("ret=%d, addr:%s, port:%d\n", ret, addr, port);
}

%s用于解析字符串(域名),%d用于解析int(端口号)。结果如下:

ret=1, addr:www.baidu.com:80, port:0

这并不是我们期望的结果,sscanf将域名和端口号都当字符串来解析了。这是因为%s对应的字符串,遇到空白字符(空格、换行)或者是'\0'才算结束。结束之后,sscanf才会去理会:%d

上述情况属于:想让字符串结束却没结束,从而解析了过多的内容。有时还会遇到相反的情况,请看下一个例子:

static void test3(void)
{
 const char *str = "how are you";
 char buf[64] = "";

 sscanf(str, "%s", buf);
 printf("%s\n", buf);
}

本想解析出完整的how are you,而输出的结果只有how

模式

使用%s来匹配字符串,会受到很大的限制。但这并不意味着sscanf不好用,其还有一种匹配字符串的方法,那就是模式匹配。

模式的格式为:%[pattern],其中的pattern用于定义一个字符集,待匹配的字符串由这个字符集组成。pattern可以是多个字符,也可以使用-定义一个范围,还可以使用^反向定义字符集。说着有点抽象,让我们看些具体的示例吧。

  • %[abcd] 匹配由a,b,c和d组成的字符串。比如对于abcdefg这个字符串,其会匹配abcd
  • %[^abcd] 当pattern由^开头时,其匹配pattern字符集以外的字符。因此,对于gfedcba这个字符串,其会匹配gfe
  • %[0-9a-fA-F] 其组成的字符集为0123456789abcdefABCDEF,其实就是16进制数字字符。

当使用-定义范围时,需要注意,起始字符必须小于结束字符。%[z-a]匹配的就不是范围,而是z,-和a这3个字符。

如果你学过正则表达式的话,对上述模式应该很熟悉。只不过,sscanf提供的模式匹配的功能比正则表达式简单的多。笔者在知道sscanf的这种隐藏用法后,屡试不爽,用的最多的就是^

现在大家知道如何解析域名和端口号了吗?

要不再思考一下?

好了,答案如下:

static void test2_fix(void)
{
 const char *str = "www.baidu.com:80";

 char addr[64] = "";
 int port = 0;
 int ret;

 ret = sscanf(str, "%[^:]:%d", addr, &port);
 printf("ret=%d, addr:%s, port:%d", ret, addr, port);
}

是不是非常简单,既然域名是:之前的内容,那就定义为%[^:]

解析GPS

现在可以用sscanf来解析GPS了,GPS样例如下:

$GNRMC,122921.000,A,3204.862246,N,11845.911047,E,0.099,191.76,280521,,E,A*00

直接上代码:

static void parse_gps(const char *gps)
{
    char valid = ' ';
    double longitude = 0;
    double latitude = 0;
    int ret;

    ret = sscanf(gps,
             "$GNRMC,%*[^,],%c,%lf,%*c,%lf,%*c,",   /* UTC,valid,latitude,ns,longitude,ew,  */
             &valid, &latitude, &longitude);

    LOG_D("parse gps(%s)", gps);


    if (ret != 3)
    {
        LOG_E("fail");
    }
    else
    {
        LOG_D("succeed, valid:%c, latitude:%lf, longitude:%lf", valid, latitude, longitude);
    }
}

下图标出了格式化参数中各格式说明符对应的字段。第二个说明符,%*[^,]用于匹配时间122921.000。与之前不同的是,这里多了一个*,这表示不用解析对应字段的内容,后面的地址参数中也没有相关变量。你看,&valid, &latitude, &longitude分别存放有效标志字符,纬度和纬度,并没有时间变量的地址。%*c同理。

测试时,用3个用例进行测试,以测试成功和失败的场景。

void parse_string_example(void)
{
    const char *strs[] =
    {
            "$GNRMC,122921.000,A,3204.862246,N,11845.911047,E,0.099,191.76,280521,,E,A*00",
            "hello world",
            "$GNRMC,,,,,,,,,,,,*00"
    };

    LOG_I("test parse string");

    for (int i = 0; i < ARRAY_SIZE(strs); i++)
    {
        parse_gps(strs[i]);
    }

}

结果如下:

文中完整的示例代码,参见笔者基于stm32f407创建的demo工程:

地址:git@gitee.com:wenbodong/mcu_demo.git
示例:examples/05_string/example.c
使用时需要打开examples/examples.h中的EXAMPLE_SHOW_STRING。




你可以添加微信17775982065为好友,注明:公司+姓名,拉进 RT-Thread 官方微信交流群!



👇 点击阅读原文进入官网

RTThread物联网操作系统 帮助您了解RT-Thread相关的资讯.
评论
  • 嘿,咱来聊聊RISC-V MCU技术哈。 这RISC-V MCU技术呢,简单来说就是基于一个叫RISC-V的指令集架构做出的微控制器技术。RISC-V这个啊,2010年的时候,是加州大学伯克利分校的研究团队弄出来的,目的就是想搞个新的、开放的指令集架构,能跟上现代计算的需要。到了2015年,专门成立了个RISC-V基金会,让这个架构更标准,也更好地推广开了。这几年啊,这个RISC-V的生态系统发展得可快了,好多公司和机构都加入了RISC-V International,还推出了不少RISC-V
    丙丁先生 2025-01-21 12:10 1229浏览
  • 临近春节,各方社交及应酬也变得多起来了,甚至一月份就排满了各式约见。有的是关系好的专业朋友的周末“恳谈会”,基本是关于2025年经济预判的话题,以及如何稳定工作等话题;但更多的预约是来自几个客户老板及副总裁们的见面,他们为今年的经济预判与企业发展焦虑而来。在聊天过程中,我发现今年的聊天有个很有意思的“点”,挺多人尤其关心我到底是怎么成长成现在的多领域风格的,还能掌握一些经济趋势的分析能力,到底学过哪些专业、在企业管过哪些具体事情?单单就这个一个月内,我就重复了数次“为什么”,再辅以我上次写的:《
    牛言喵语 2025-01-22 17:10 494浏览
  • 不让汽车专美于前,近年来哈雷(Harley-Davidson)和本田(Honda)等大型重型机车大厂的旗下车款皆已陆续配备车载娱乐系统与语音助理,在路上也有越来越多的普通机车车主开始使用安全帽麦克风,在骑车时透过蓝牙连线执行语音搜寻地点导航、音乐播放控制或免持拨打接听电话等各种「机车语音助理」功能。客户背景与面临的挑战以本次分享的客户个案为例,该客户是一个跨国车用语音软件供货商,过往是与车厂合作开发前装车机为主,且有着多年的「汽车语音助理」产品经验。由于客户这次是首度跨足「机车语音助理」产品,因
    百佳泰测试实验室 2025-01-24 17:00 194浏览
  • 项目展示①正面、反面②左侧、右侧项目源码:https://mbb.eet-china.com/download/316656.html前言为什么想到要做这个小玩意呢,作为一个死宅,懒得看手机,但又想要抬头就能看见时间和天气信息,于是就做个这么个小东西,放在示波器上面正好(示波器外壳有个小槽,刚好可以卡住)功能主要有,获取国家气象局的天气信息,还有实时的温湿度,主控采用ESP32,所以后续还可以开放更多奇奇怪怪的功能,比如油价信息、股票信息之类的,反正能联网可操作性就大多了原理图、PCB、面板设计
    小恶魔owo 2025-01-25 22:09 615浏览
  • 随着AI大模型训练和推理对计算能力的需求呈指数级增长,AI数据中心的网络带宽需求大幅提升,推动了高速光模块的发展。光模块作为数据中心和高性能计算系统中的关键器件,主要用于提供高速和大容量的数据传输服务。 光模块提升带宽的方法有两种:1)提高每个通道的比特速率,如直接提升波特率,或者保持波特率不变,使用复杂的调制解调方式(如PAM4);2)增加通道数,如提升并行光纤数量,或采用波分复用(CWDM、LWDM)。按照传输模式,光模块可分为并行和波分两种类型,其中并行方案主要应用在中短距传输场景中成本
    hycsystembella 2025-01-25 17:24 473浏览
  • 故障现象 一辆2007款日产天籁车,搭载VQ23发动机(气缸编号如图1所示,点火顺序为1-2-3-4-5-6),累计行驶里程约为21万km。车主反映,该车起步加速时偶尔抖动,且行驶中加速无力。 图1 VQ23发动机的气缸编号 故障诊断接车后试车,发动机怠速运转平稳,但只要换挡起步,稍微踩下一点加速踏板,就能感觉到车身明显抖动。用故障检测仪检测,发动机控制模块(ECM)无故障代码存储,且无失火数据流。用虹科Pico汽车示波器测量气缸1点火信号(COP点火信号)和曲轴位置传感器信
    虹科Pico汽车示波器 2025-01-23 10:46 321浏览
  • 高速先生成员--黄刚这不马上就要过年了嘛,高速先生就不打算给大家上难度了,整一篇简单但很实用的文章给大伙瞧瞧好了。相信这个标题一出来,尤其对于PCB设计工程师来说,心就立马凉了半截。他们辛辛苦苦进行PCB的过孔设计,高速先生居然说设计多大的过孔他们不关心!另外估计这时候就跳出很多“挑刺”的粉丝了哈,因为翻看很多以往的文章,高速先生都表达了过孔孔径对高速性能的影响是很大的哦!咋滴,今天居然说孔径不关心了?别,别急哈,听高速先生在这篇文章中娓娓道来。首先还是要对各位设计工程师的设计表示肯定,毕竟像我
    一博科技 2025-01-21 16:17 241浏览
  •     IPC-2581是基于ODB++标准、结合PCB行业特点而指定的PCB加工文件规范。    IPC-2581旨在替代CAM350格式,成为PCB加工行业的新的工业规范。    有一些免费软件,可以查看(不可修改)IPC-2581数据文件。这些软件典型用途是工艺校核。    1. Vu2581        出品:Downstream     
    电子知识打边炉 2025-01-22 11:12 465浏览
  • 飞凌嵌入式基于瑞芯微RK3562系列处理器打造的FET3562J-C全国产核心板,是一款专为工业自动化及消费类电子设备设计的产品,凭借其强大的功能和灵活性,自上市以来得到了各行业客户的广泛关注。本文将详细介绍如何启动并测试RK3562J处理器的MCU,通过实际操作步骤,帮助各位工程师朋友更好地了解这款芯片。1、RK3562J处理器概述RK3562J处理器采用了4*Cortex-A53@1.8GHz+Cortex-M0@200MHz架构。其中,4个Cortex-A53核心作为主要核心,负责处理复杂
    飞凌嵌入式 2025-01-24 11:21 293浏览
  • 书接上回:【2022年终总结】阳光总在风雨后,启航2023-面包板社区  https://mbb.eet-china.com/blog/468701-438244.html 总结2019,松山湖有个欧洲小镇-面包板社区  https://mbb.eet-china.com/blog/468701-413397.html        2025年该是总结下2024年的喜怒哀乐,有个好的开始,才能更好的面对2025年即将
    liweicheng 2025-01-24 23:18 350浏览
  • 前篇文章中『服务器散热效能不佳有解吗?』提到气冷式的服务器其散热效能对于系统稳定度是非常重要的关键因素,同时也说明了百佳泰对于散热效能能提供的协助与服务。本篇将为您延伸说明我们如何进行评估,同时也会举例在测试过程中发现的问题及改善后的数据。AI服务器的散热架构三大重点:GPU导风罩:尝试不同的GPU导风罩架构,用以集中服务器进风量,加强对GPU的降温效果。GPU托盘:改动GPU托盘架构,验证出风面积大小对GPU散热的影想程度。CPU导风罩:尝试封闭CPU导风罩间隙,集中风流,验证CPU降温效果。
    百佳泰测试实验室 2025-01-24 16:58 189浏览
  •  万万没想到!科幻电影中的人形机器人,正在一步步走进我们人类的日常生活中来了。1月17日,乐聚将第100台全尺寸人形机器人交付北汽越野车,再次吹响了人形机器人疯狂进厂打工的号角。无独有尔,银河通用机器人作为一家成立不到两年时间的创业公司,在短短一年多时间内推出革命性的第一代产品Galbot G1,这是一款轮式、双臂、身体可折叠的人形机器人,得到了美团战投、经纬创投、IDG资本等众多投资方的认可。作为一家成立仅仅只有两年多时间的企业,智元机器人也把机器人从梦想带进了现实。2024年8月1
    刘旷 2025-01-21 11:15 995浏览
我要评论
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦