字符串格式化

 sprintf

_snprintf(snprintf)

std::stringstream

std::strstream

boost::lexical_cast

boost::format

Cstring::Format

 

1Sprintf

使用sprintf 不安全,轻则破坏数据的准确性,重则程序崩溃。请看下面的程序片段:

CString strTest = "hello";

1     int a = 10;

2     char tbuf[10];

3    sprintf(tbuf, "%s,%d", "0123456789 0123456789 0123456789 0123456789",a);

4     printf("%s/n", tbuf);

5     if (10 == a)

6            strTest.Insert(0, "abc");

7     printf("%s/n", strTest.GetBuffer(0));

      

       编译一下,没问题,0 error(s), 0 warning(s)

       看看输出来的结果,很幸运,执行第4行输出tbuf格式的字符串,跟想要的结果一致的。但奇怪的问题出现了,a的数值只有初始化赋值为10,没其他地方给它赋值了,为声明不会执行第6行,更奇怪的是执行第7行抛异常了。其实原因就是,sprintf,在幕后偷偷的修改了a的值,并且破坏了strTest这个对象。

       类似的错误还有想这样的代码:

int i, a[10];

for (i=0; i<=10; ++i)

       a[i] = 0;

本来想给数组a初始化,没想到的是,程序进入了一个死循环。

 

i (a[10])

0x0012ff54

a[9]

0x0012ff50

a[8]

0x0012ff4C

a[7]

0x0012ff48

通常情况下,栈是往内存的低地址方向增长的,也就是说,现压栈的内容存放在高地址区域,后压栈的内容存放在低地址区域。

   

2_snprintf

       sprintf处的代码改为用_snprintf,看看结构如何。_snprintf(tbuf,sizeof(tbuf)-1, "%s,%d", "0123456789 0123456789 0123456789 0123456789",a);

我们一看结果就知道是缓冲区不够了,这样容易发现问题,也不会破坏程序内存。注意,即便这样,仍然还存在另外一种出错的可能,即调用者将缓冲区的长度搞错了。

 

3Std::stringstream

ostringstream temp;

temp<< "0123456789 0123456789 0123456789 0123456789,"<<a;

string s = temp.str()

sprintf std::stringstream的优缺点:

1)sprintf 具有易用性和清晰性,而std::stringstream 比较臃肿了,至少要三行代码实现。

2)Sprintf 字节利用缓冲区,效率高。而std::stringstream要尽管多次拷贝,有可能还要多次动态分配内存。

3)std::stringstream 动态分配内存,所以长度是安全的,不会出现破获内存的情况

4)std::stringstream 模版类型安全,方便。而sprintf有可能把一个整数格式到 %s

 

 

4.std::strsteam

char tbuf[10];

std::strstream temp(tbuf, sizeof(tbuf));

temp<< "0123456789 0123456789 0123456789 0123456789,"<<a<<’/0’;

       效果跟_snprintf差不多,但是它要自己手动加上 ‘/0’ 字符串结束符。而且至少要写两行代码。优点也是长度安全,类型安全,有模版的亲和性。我个人来说不喜欢用。

 

 

5.boost::lexical_cast

string s = "0123456789 0123456789 0123456789 0123456789,"+boost::lexical_cast<string>(a);

       实际上lexical_cast的目的只是为了将数据从一个可流化的类型转换为另一个可流化的类型。就类似atoi, itoa, 等之类的函数。做格式化一串数据感觉就不是很好了。

 

 

6.boost::format

boost::format fs = boost::format("%1%,%2%") %"0123456789 0123456789 0123456789 0123456789" % a;  

//string ss = fs.str();

       boost::format简洁优雅,长度安全,类型安区。只是由于内存是动态分配的,效率上比sprintf, _snprintf稍低一点。但如果我们的项目使用了boost库,用boost::format总体上比sprintf,_snprintf 都好一点。

 

7MFC中的CString

如果决定用mfc库,并且以后不会移植的话,最好就用Cstring

CString s;

s.Format("%s,%d", "0123456789 0123456789 0123456789 0123456789", a);

但是它要表明格式的类型和sprintf, _snprintf一样,类型并不是总是安全的。

 

总结:通常情况下考虑效率使用_snprintf最好,如果使用boost库,且对运行效率没有苛刻的要求请使用boost::format 如果使用了MFC,且又不考虑以后的移植,请使用Cstring

 

补充:我们经常对字符串的拷贝同样有两c标准函数:

char *strcpy( char * strDest, const char *strSource );

char *strncpy( char *strDest, const char *strSource, size_t count );

万一我们的strDest缓冲区长度不够,同样会破坏内存,使程序崩溃。

例如:

char tbuf[10]={0};

string strSrc = "hello, I'm string";

strcpy(tbuf, strSrc.c_str());

改为

strncpy(tbuf, strSrc.c_str(), min(sizeof(tbuf)-1,strSrc.size()));

那就安全了。

上面这些都是一些自己在开发过程中的总结和心得,希望对朋友们有帮助!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值