需要几个理由:
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道理也是一样的,就不贴出来了
来自:http://space.itpub.net/10321586/viewspace-364629
重写sprintf
最新推荐文章于 2024-07-09 10:47:13 发布