【发布时间】:2017-09-02 22:01:40
【问题描述】:
我有一个从 GSM 模块获取的时间和日期字符串。
这是时间和日期的格式:
"yy/MM/dd,hh:mm:ss±zz"
还有一个示例字符串:
"+CCLK: "17/04/07,12:57:43+03""
我需要对其进行解析并将其转换为 Unix 时间戳。我有下面的实现,它运行良好:
时间结构:
struct timeStruct{
uint16 Year;
uint8 Month;
uint8 Day;
uint8 Hour;
uint8 Minute;
uint8 Second;
};
GSM 时钟命令解析器:
void load_clock(void)
{
struct timeStruct gsmTimeStruct;
/* Parse year,month,day,hour,minute,second and GMT timezone */
sscanf(gsm_clk_string, "+CCLK: \"%hd/%d/%d,%d:%d:%d%c%d", &(gsmTimeStruct.Year), &(gsmTimeStruct.Month), &(gsmTimeStruct.Day), &(gsmTimeStruct.Hour), &(gsmTimeStruct.Minute), &(gsmTimeStruct.Second),&CONEZONE,&TIMEZONE);
unixTime = (uint32_t)RTC_DateTimeToUnix(gsmTimeStruct);
/* Collect GMT difference */
if(CONEZONE == '+')
unixTime = unixTime - TIMEZONE*3600;
else
unixTime = unixTime + TIMEZONE*3600;
}
sscanf 函数从闪存中消耗 1776 字节,这对于这个 MCU 来说是一个巨大的数字。 Unix 转换函数也消耗大约 700 Bytes,但它有点正常,因为它有一些数学计算。
在不消耗如此大的闪存使用量的情况下解析这个字符串有什么好主意吗?
注意:命令字符串的错误检查不是问题,因为 GSM 模块已经这样做了。因此,如果我们想更改解析实现,我们不必这样做。我们可以假设这些数字一直存在。
编辑:我在 sn-p 下试过,效果很好。它节省了 1.7 KB 内存,但我不确定这是否是正确的方法。
gsmTimeStruct.Year = (gsm_clk_string[8]-'0')*10 + (gsm_clk_string[9]-'0');
gsmTimeStruct.Month = (gsm_clk_string[11]-'0')*10 + (gsm_clk_string[12]-'0');
gsmTimeStruct.Day = (gsm_clk_string[14]-'0')*10 + (gsm_clk_string[15]-'0');
gsmTimeStruct.Hour = (gsm_clk_string[17]-'0')*10 + (gsm_clk_string[18]-'0');
gsmTimeStruct.Minute = (gsm_clk_string[20]-'0')*10 + (gsm_clk_string[21]-'0');
gsmTimeStruct.Second = (gsm_clk_string[23]-'0')*10 + (gsm_clk_string[24]-'0');
TIMEZONE = (gsm_clk_string[26]-'0')*10 + (gsm_clk_string[27]-'0');
CONEZONE = gsm_clk_string[25];
【问题讨论】:
-
每个 ASCII 数字都可以减去
'0'(0x30) 进行转换。顺便说一句,它对 SO 来说太宽泛了 -
如果
sscanf太大,你会被困在手动沿着字符串移动并用strtoul/atoi或类似的东西解析每个数字(并且你负责错误检查,如果可能出现错误),或通过 ASCII 数学进行手动文本转换。它是否真的节省了任何东西是有待商榷的(大概sscanf可能会在其他地方重复使用,从而降低每次使用的摊销成本)。你确定你非常需要空间,还是这只是过早的优化? -
自己解析。
-
一个简单的
for是(可能)更小的解决方案。 -
阿卜杜拉:小技巧:
(a-'0')*10+(b-'0')和a*10+b-(11*'0')一样,省了一个减法。当然,您的编译器可能会发现这种优化;你可以检查编译的代码。