zoukankan      html  css  js  c++  java
  • 对格式化字符串的一些思考

    在最近工作中,在拼接sql语句时,用到格式化字符串,经过和同事讨论后,有了新的思考。这个点在之前都是参照已有来使用,没有深入去追究,本次全面了解下。

    格式化字符串

    在实际工程中,格式化字符串一般用在打印日志、拼接字符串等,目前在工程中常见的是这样用的:

    char szbuf[128] = {0};
    snprintf(szbuf, sizeof(szbuf) - 1, "log:%d %s", 1,"XXXX");
    
    

    按照上述格式化完后,对缓冲区szBuf进行各种操作。这里面有几点疑问:

    1. 这里没有判断snprintf的返回值,需不需要判断?
    2. snprintf的第二个参数为什么要减1?
    3. 没有考虑格式化完整性

    下面依次分析。

    snprintf的返回值代表什么含义

    linux环境上,snprintf的函数原型如下。

    int snprintf(char* str, size_t size, const char* format, ...);
    

    查阅snprintfman手册,对返回值有如下说明:

    snprintf返回值说明.jpg

    该函数的返回值表示待格式化字符串的长度,不包括结束分隔符。这是什么意思呢?举个例子:

    • 格式化 abcd 字符串,返回值是4,表示待格式化的字符个数为4。
    • 格式化 01234567890 字符串,返回值为10。

    这个返回值和传入的缓冲区长度size没有任何关系的,它只与待格式化字符串的长度有关。

    另一个需要注意的点是,这个函数在格式化时,不会往缓冲区str中写入多于size大小的数据,包括在内。这就是说,外部传入size大小的缓冲区,该函数能够保证在size大小内,填充包含,至于有没有截断,需要使用者来进一步判断。

    至此,可解答上面两个问题:

    • snprintf的返回值表示待格式化字符串的长度
    • snprintf的第二个参数没必要减1,按照输入缓冲区长度传入即可。

    第三个问题,判断格式化后是否被截断。从上述说明来看,如果返回值大于等于入参size时,表明格式化输出被截断。另外一种情况,如果遇到输出错误,会返回一个负值。

    因此,可总结出如下代码:

    const int nBufSize = 256;
    char szBuf[nBufSize] = {0};
    int nRet = snprintf(szBbuf, nBufSize, "XXXX", "XXXX");
    if (nRet >= nBufSize || nRet < 0)
    {
    	// 格式化被截断或者格式化失败
    }
    else
    {
    	// 正常格式化,走正常流程
    }
    
    

    截断后的处理

    一般来说,我们在格式化字符串时,提供的缓冲区都足够大,保证不会发生截断。当有些格式化数据由外部传入,当这些数据含有异常、超长数据时,比如一个无效的double值,同时此时缓冲区较小,就可能会被截断。

    截断后要如何处理呢?这里不好一概而论,得区分格式化字符串的上下文场景

    • 如果是执行sql,这种情况下,无论被截断的是哪部分,这个sql都不安全l,不允许后续使用。发生此种情况的截断,需要记录错误日志后退出。

    • 如果是记录日志,日志是为了检查系统状态,即使被截断,前面信息也是有效的,这种场景也要记录为错误日志,但后续逻辑可继续运行。

    以上是我目前想到的两种场景,可能还有其他场景。就比如执行sql来说,如果是很简单,很显而易见的sql,例如根据UserId从数据库中查找相关信息,这种很简单的sql语句,要不要判断截断呢?如果这种简单的不判断,那复杂到什么程度才判断呢?感觉这样判断,会让逻辑依赖于数据,这是不好的做法。笔者在这里无法给出绝对的结论,不同场景下不同的处理,能够达成团队一致即可。

    这里说一句,即使使用存储过程,缩短sql的长度,但只能缓解拼接截断发生的概览,对于有动态数据的查询,还是要小心。

    总结

    本文总结了在格式化字符串时的一些注意事项,给出自认为比较正确的做法,并就格式化截断后的处理进行了简短讨论。

    另外一点,在开发过程中,不能有照抄心态。这种心态,会参照现有工程中类似逻辑的做法,其他地方是怎么用的,所以这里也这么用。这种心态或者做法,不会使得代码质量有所提高,反而会复制粘贴可能的错误或不规范的用法。

  • 相关阅读:
    perl next和last
    用 Flask 来写个轻博客 (26) — 使用 Flask-Celery-Helper 实现异步任务
    mysql 更新唯一主键列 被堵塞
    perl + 匹配前导模式一次或者多次
    跨域
    日志处理
    FineBI:一个简单易用的自助BI工具
    FineBI:一个简单易用的自助BI工具
    bootstrap-treeview
    Bootstrap树形菜单插件TreeView.js使用方法详解
  • 原文地址:https://www.cnblogs.com/cherishui/p/14167732.html
Copyright © 2011-2022 走看看