重写sprintf


需要几个理由:
1、sprintf不能返回格式化结果串的实际长度
2、sprintf总是把NULL指针格式化为(null)
3、sprintf的%S格式化只能处理UNICODE到SBCS转换,不能处理UNICODE到MBCS的转换
4、sprintf处理浮点数,一不小心就拖出个长长的尾巴来

格式串的规则定义
//%[flag] [width] [.precision] [{h | l | I64 | L}]type
//flags: -,+,0,' ',#
//width:
//precision
//type:c,C,d,i,o,u,x,X,e,E,f,g,s,S

//定义扫描机的状态
typedef enum{
    XS_SKIP = 0,     //扫描前缀串
    XS_PROC = 1,     //需处理当前格式或前缀串
    XS_FLAG = 2,     //扫描添位符
    XS_WIDTH = 3,     //扫描占宽
    XS_PREC = 4,     //扫描精度
    XS_SIZE= 5,     //扫描整形指示
    XS_TYPE = 6,     //扫描格式符
    XS_TERM = 7     //扫描终止
}XF_STATUS;

//定义扫描机的动作
typedef enum{
    XO_PAUSE = 0,    //暂停
    XO_CONTINUE = 1    //继续
}XF_OPERA;

//判断是否添位符
#define s_is_flag(ch)    ((ch == '0' || ch == ' ' || ch == '#')? 1 : 0)
//判断是否是数字
#define s_is_digit(ch)    ((ch >= '0' && ch <= '9')? 1 : 0)
//判断是否是整形指示
#define s_is_size(ch)    ((*token == 'h' || *token == 'l')? 1 : 0)
//判断是否是格式符
#define s_is_type(ch)    ((ch == 'c' || ch == 'C' || ch == 'd' || ch == 'u' || ch == 'x' || ch == 'X' || ch == 'f'|| ch == 's' || ch == 'S')? 1 : 0)

int xsprintf(char* buf,const char* fmt,...)
{
    va_list arg;
    int total = 0;
    char xf_flag = 0;
    int xf_width = 0;
    int xf_prec = 0;
    char xf_size = 0;
    char xf_type = 0;
    char tk_width[NUM_LEN + 1],tk_prec[NUM_LEN + 1];
    int width_count = 0;
    int prec_count = 0;
    int tk_count = 0;

    //初始化
    XF_STATUS xs = XS_SKIP;
    XF_OPERA xo = XO_PAUSE;

    char* token = (char*)fmt;

    va_start(arg,fmt);

    while(xs != XS_TERM)
    {
        switch(xs)
        {
        case XS_SKIP:
            if(*token == '%' && *(token + 1) != '%')
            {
                if(!tk_count)
                {
                    xs = XS_FLAG; //没有前缀串则过渡到格式串扫描
                    xo = XO_CONTINUE;
                }else
                {
                    xs = XS_PROC; //有前缀串则先处理前缀串
                    xo = XO_PAUSE;
                }
            }else if(*token == '\0')
            {
                xs = XS_PROC; //过渡到处理前缀串
                xo = XO_PAUSE;
            }else
            {
                xs = XS_SKIP; //继续前缀串扫描
                xo = XO_CONTINUE;
            }
            break;
        case XS_FLAG:
            if(s_is_flag(*token))
            {
                xf_flag = *token;
                xs = XS_FLAG; //继续占位符扫描
                xo = XO_CONTINUE;
            }else
            {
                xs = XS_WIDTH; //过渡到占位宽扫描
                xo = XO_PAUSE;
            }
            break;
        case XS_WIDTH:
            if(s_is_digit(*token))
            {
                tk_width[width_count ++] = *token;
                xs = XS_WIDTH; //继续占位宽扫描
                xo = XO_CONTINUE;
            }else if(*token == '.')
            {
                xs = XS_PREC; //过渡到精度扫描
                xo = XO_CONTINUE;
            }else
            {
                xs = XS_SIZE; //过渡到整形指示扫描
                xo = XO_PAUSE;
            }
            break;
        case XS_PREC:
            if(s_is_digit(*token))
            {
                tk_prec[prec_count ++] = *token;
                xs = XS_PREC; //继续精度扫描
                xo = XO_CONTINUE;
            }else
            {
                xs = XS_SIZE; //过渡到整形指示扫描
                xo = XO_PAUSE;
            }
            break;
        case XS_SIZE:
            if(s_is_size(*token))
            {
                xf_size = *token;
                xs = XS_TYPE; //过渡到格式符扫描
                xo = XO_CONTINUE;
            }else
            {
                xs = XS_TYPE; //过渡到格式符扫描
                xo = XO_PAUSE;
            }
            break;
        case XS_TYPE:
            if(s_is_type(*token))
            {
                xf_type = *token;
                xs = XS_PROC; //过渡到处理格式
                xo = XO_CONTINUE;
            }else
            {
                xs = XS_PROC; //无效格式符,则过渡到前缀串处理
                xo = XO_CONTINUE;
            }
            break;
        case XS_PROC:
            if(xf_type) //处理格式串
            {
                tk_width[width_count] = '\0';
                xf_width = atoi(tk_width);
                tk_prec[prec_count] = '\0';
                xf_prec = atoi(tk_prec);
                if(!xf_flag)
                    xf_flag = ' ';
                if(xf_prec && xf_prec > MAX_PREC) //限制最大精度为6
                    xf_prec = MAX_PREC;
                //处理这个单元的格式串
                total += _tk_sprintf((buf)? (buf + total) : NULL,xf_flag,xf_width,xf_prec,xf_size,xf_type,&arg);

                if(*token == '\0')
                    xs = XS_TERM;
                else
                    xs = XS_SKIP;
                xo = XO_PAUSE;
            }else //处理前缀串
            {
                if(buf)
                {
                    memcpy((void*)(buf + total),(void*)(token - tk_count),tk_count * sizeof(char));
                    buf[total + tk_count] = '\0';
                }
                total += tk_count;

                if(*token == '\0')
                    xs = XS_TERM;
                else
                    xs = XS_SKIP;
                xo = XO_PAUSE;
            }
            xf_flag = 0;
            xf_width = 0;
            xf_prec = 0;
            xf_size = 0;
            xf_type = 0;
            width_count = prec_count = tk_count = 0;

            break;
        }

        if(xo == XO_CONTINUE)
        {
            token ++;
            tk_count ++;
        }
    }

    va_end(arg);

    return total;
}


int _tk_sprintf(char* buf,char flag,int width,int prec,char size,char type,va_list* parg)
{
    wchar_t wch;
    char tmp[NUM_LEN + 1] = {0};
    int len,pos;
    char sign;
    short s;
    int i;
    unsigned short us;
    unsigned int ui,xi;
    double dbl;
    char* sz;
    wchar_t* wsz;

    switch(type)
    {
    case 'c':
        if(buf)
        {
            buf[0] = va_arg(*parg,char);
            buf[1] = '\0';
        }
        return 1;
    case 'C':
        wch = va_arg(*parg,wchar_t);
        len = WideCharToMultiByte(CP_ACP,0,&wch,1,NULL,0,NULL,NULL);
        if(buf)
        {
            WideCharToMultiByte(CP_ACP,0,&wch,1,buf,len,NULL,NULL);
            buf[len] = '\0';
        }
        return len;
    case 'd':
        len = 0;
        sign = 0;
        pos = 0;
        if(size == 'h')
        {
            s = va_arg(*parg,short);
            if(s < 0)
            {
                sign = '-';
                s = 0 - s;
            }
            
            while(s)
            {
                tmp[len ++] = s % 10 + 48;
                s /= 10;
            }
        }else
        {
            i = va_arg(*parg,int);
            if(i < 0)
            {
                sign = '-';
                i = 0 - i;
            }

            while(i)
            {
                tmp[len ++] = i % 10 + 48;
                i /= 10;
            }
        }
        if(sign)
        {
            if(buf)
                buf[pos] = sign;
            pos ++;
            width --;
        }
        width -= len;
        while(width > 0)
        {
            if(buf)
                buf[pos] = '0';
            pos ++;
            width --;
        }
        while(len--)
        {
            if(buf)
                buf[pos] = tmp[len];
            pos ++;
        }
        if(buf)
            buf[pos] = '\0';
        return pos;
    case 'u':
        len = 0;
        sign = 0;
        pos = 0;
        if(size == 'h')
        {
            us = va_arg(*parg,unsigned short);
            while(us)
            {
                tmp[len ++] = us % 10 + 48;
                us /= 10;
            }
        }else
        {
            ui = va_arg(*parg,unsigned int);
            while(ui)
            {
                tmp[len ++] = ui % 10 + 48;
                ui /= 10;
            }
        }
        width -= len;
        while(width > 0)
        {
            if(buf)
                buf[pos] = '0';
            pos ++;
            width --;
        }
        while(len--)
        {
            if(buf)
                buf[pos] = tmp[len];
            pos ++;
        }
        if(buf)
            buf[pos] = '\0';
        return pos;
    case 'x':
    case 'X':
        len = 0;
        sign = 0;
        pos = 0;

        if(buf)
        {
            buf[0] = '0';
            buf[1] = type;
        }
        pos += 2;

        ui = va_arg(*parg,unsigned int);
        while(ui)
        {
            us = ui % 16;
            if(type == 'x')
                tmp[len ++] = (us < 9)? (us + 48) : (us + 87);
            else
                tmp[len ++] = (us < 9)? (us + 48) : (us + 55);
            ui /= 16;
        }

        width -= len;
        while(width > 0)
        {
            if(buf)
                buf[pos] = '0';
            pos ++;
            width --;
        }
        while(len--)
        {
            if(buf)
                buf[pos] = tmp[len];
            pos ++;
        }
        if(buf)
            buf[pos] = '\0';
        return pos;
    case 'f':
        len = 0;
        sign = 0;
        pos = 0;

        dbl = va_arg(*parg,double);
        if(dbl < 0)
        {
            sign = '-';
            dbl = 0 - dbl;
        }

        xi = (int)dbl;

        dbl -= (double)xi;
        if(prec)
            s = (short)prec;
        else
            s = MAX_PREC;
        while(s--)
            dbl *= 10.0;

        ui = (int)floor(dbl + 0.5);

        if(!prec)
        {
            while(!(ui % 10))
                ui /= 10;
        }

        while(ui)
        {
            tmp[len ++] = ui % 10 + 48;
            ui /= 10;
        }
        if(len)
            tmp[len ++] = '.';

        while(xi)
        {
            tmp[len ++] = xi % 10 + 48;
            xi /= 10;
            width --;
        }

        if(sign)
        {
            if(buf)
                buf[pos] = sign;
            pos ++;
            width --;
        }

        while(width > 0)
        {
            if(buf)
                buf[pos] = '0';
            pos ++;
            width --;
        }

        while(len--)
        {
            if(buf)
                buf[pos] = tmp[len];
            pos ++;
        }
        if(buf)
            buf[pos] = '\0';
        return pos;
    case 's':
        len = 0;
        sign = 0;
        pos = 0;

        sz = va_arg(*parg,char*);
        if(!sz)
        {
            if(buf)
                buf[pos] = '\0';
            return pos;
        }
       
        len = strlen(sz);
        width -= len;
        while(width > 0)
        {
            if(buf)
                buf[pos] = flag;
            pos ++;
            width --;
        }

        if(buf)
            memcpy((void*)(buf + pos),(void*)sz,len * sizeof(char));
        pos += len;
        if(buf)
            buf[pos] = '\0';
        return pos;
    case 'S':
        len = 0;
        sign = 0;
        pos = 0;

        wsz = va_arg(*parg,wchar_t*);
        if(!wsz)
        {
            if(buf)
                buf[pos] = '\0';
            return pos;
        }

        ui = wcslen(wsz);
        len = WideCharToMultiByte(CP_ACP,0,wsz,ui,NULL,0,NULL,NULL);
        width -= len;
        while(width > 0)
        {
            if(buf)
                buf[pos] = flag;
            pos ++;
            width --;
        }
       
        if(buf)
            WideCharToMultiByte(CP_ACP,0,wsz,ui,buf + pos,len,NULL,NULL);
        pos += len;
        if(buf)
            buf[pos] = '\0';
        return pos;
    }
    return 0;
}

这样:
int len = xspritf(NULL,"这次要分配%d个字节,18)可以返回结果串的精确长
xsprintf(buf,"a=%s",NULL)不会格式成a=(null)
xsprintf(buf,"a=%f",8.0/9.0)不会返回长长的尾巴了
xsprintf(buf,"%svs%S","俄罗斯",L"荷兰")可以处理MBCS和UNICODE
少了些麻烦
不过没有支持[e,E,f,g,]的格式符,抱歉了
UICODE版的xwprintf道理也是一样的,就不贴出来了

 

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/10321586/viewspace-364629/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/10321586/viewspace-364629/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值