【问题标题】:Minimizing Memory Consumption on sscanf最小化 sscanf 的内存消耗
【发布时间】: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') 一样,省了一个减法。当然,您的编译器可能会发现这种优化;你可以检查编译的代码。

标签: c parsing memory scanf


【解决方案1】:

我想你只需要这个,只需相应地移动 char ptr 即可。 在下面的代码示例中,我假设gsm_clk_string 中的第一个字符是"

/*                     "+CCLK: "2017/04/07, 12:57:43+03""    */
/*  gsm_clk_string[] : 0123456789012345678902134567890123    */

char *ptr;

ptr = gsm_clk_string;
ptr += 9;

gsmTimeStruct.Year = (uint16) ((*ptr - '0') * 1000);
ptr++;
gsmTimeStruct.Year += (uint16) ((*ptr - '0') * 100);
ptr++;
gsmTimeStruct.Year += (uint16) ((*ptr - '0') * 10);
ptr++;
gsmTimeStruct.Year += (uint16) ((*ptr - '0'));

ptr += 2;

gsmTimeStruct.Month = (uint8) ((*ptr - '0') * 10);
ptr++;
gsmTimeStruct.Month += (uint8) (*ptr - '0');

ptr += 2;

gsmTimeStruct.Day = (uint8) ((*ptr - '0') * 10);
ptr++;
gsmTimeStruct.Day += (uint8) (*ptr - '0');

ptr += 3;

gsmTimeStruct.Hour = (uint8) ((*ptr - '0') * 10);
ptr++;
gsmTimeStruct.Hour += (uint8) (*ptr - '0');

ptr += 2;

gsmTimeStruct.Minute = (uint8) ((*ptr - '0') * 10);
ptr++;
gsmTimeStruct.Minute += (uint8) (*ptr - '0');

ptr += 2;

gsmTimeStruct.Second = (uint8) ((*ptr - '0') * 10);
ptr++;
gsmTimeStruct.Second += (uint8) (*ptr - '0');

ptr++;

if ( *ptr == '+' )
{
   ptr++;
   TIMEZONE = (whatever_type_TimeZone) ((*ptr - '0') * 10);
   ptr++
   TIMEZONE += (whatever_type_TimeZone) ((*ptr - '0'));

   unixTime = (uint32_t) RTC_DateTimeToUnix( gsmTimeStruct ) - ( TIMEZONE * 3600 );    
}
else if ( *ptr == '-' )
{
   ptr++;
   TIMEZONE = (whatever_type_TimeZone) ((*ptr - '0') * 10);
   ptr++
   TIMEZONE += (whatever_type_TimeZone) ((*ptr - '0'));

   unixTime = (uint32_t) RTC_DateTimeToUnix( gsmTimeStruct ) + ( TIMEZONE * 3600 );
}
else
{
    /* problem */
}

【讨论】:

  • 你应该使用像int convert(char *buffer, int digit)这样的函数来改进你的代码
  • 我不知道函数,但我肯定至少会使用循环。我怀疑其中任何一个都可能产生更小的编译代码。
  • 我手动编写并编辑了我的问题。我也会试试这个,看看有什么不同。另外,如果你们能在那个 sn-p 上说出你们的 cmets,我将不胜感激。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-08
  • 1970-01-01
  • 1970-01-01
  • 2012-11-16
  • 2017-10-07
相关资源
最近更新 更多