字符串格式化大比拼

来源:互联网 发布:cf辅助源码群 编辑:程序博客网 时间:2024/05/16 19:42

在平常的编程过程中,总免不了格式化字符串。而作为C++程序员,是快乐的也是痛苦不的。快乐是因为我们有多种方式来达到目的,痛苦也是因为有多种方式而难以决策,总是在思索效率与优雅!C++逐步“沦落”的原因也正是因为她的高贵的气质(难以驾驭),时尚的发型(模板),狂野的性格(内存)。哎,迷恋它,她就是我的传说

先给出一张表格,然后再来一一琢磨!

 sprintfsnprintfstringstreamstrsteramtr2::lexical_castboost::format备注易用性YYNNYN 无需额外内存YYNYNN 缓冲区安全NYYYYY 类型安全NNYYYY 是否可用于模板NNYYYY 效率      耗时取样,以sprintf为基准

 

一、sprintf

先来看个成功案例

void FormatString(int i, char *pBuf)
{
sprintf(pBuf, "%4d", i);
}

char buf[5] = {0};
FormatString(10, buf);
cout << buf << endl;
输出:  10

sprintf的易用性是无需多言的,任何学过C语言的童鞋都能搞定,而且不需要额外的内存空间(除开我们制定的Buffer区)。但是,也正是因为他来自C,所以就有一些不良嗜好:

1) 缓冲区溢出:

void FormatString(int i, char *pBuf)
{
sprintf(pBuf, "%4d", i);
}
char buf[3] = {0}; // 这里所指定的缓冲区不能满足%4d的空间
FormatString(10, buf);
cout << buf << endl;
完了,内存遭到了破坏,在VS2008下测试,运行时会提示“Run-Time Check Failure #2 - Stack around the variable 'buf' was corrupted.”

2)类型安全

void FormatString(int i, char *pBuf)
{
sprintf(pBuf, "%4c", i); // 注意,这里是%4c
}
char buf[5] = {0};
FormatString(10, buf);
cout << buf << endl; // 现在什么也不输出
由于printf家族都是使用的C可变参数列表,编译期间不检查参数列表(好像有个lint的工具可以检查,没用过。)
 
 
3)使用模板来解决类型安全
template<typename T>
void FormatString(T value, char *pBuf); // 主模板进行声明,不定义

template<>
void FormatString<int>(int value, char *pBuf) // 特化
{
sprintf(pBuf, "%d", value);
}
template<>
void FormatString<char>(char value, char *pBuf) // 特化

{
sprintf(pBuf, "%c", value);
}

template<>
void FormatString<char *>(char *value, char *pBuf) // 特化

{
sprintf(pBuf, "%s", value);
}
 
够了,不想再写了!来看其他的替代方案吧!
 
 
二、snprintf(M$用的是_snprintf)
void FormatString(int i, char *pBuf, int nBufLen)
{
_snprintf(pBuf, nBufLen, "%4d", i); // MS用的带下划线的_snprintf
}
char buf[5] = {0};
FormatString(10, buf, sizeof(buf) / sizeof(buf[0]));
cout << buf << endl;
正确输出:  10
在C99中snprintf成为了标准的一部分,这样,就能避免程序缓冲区溢出,除非故意把指定缓冲区长度搞错。但是还是不能解决类型安全的为题
 
 
三、std::stringstream
看列子
void FormatString(int i, string &str)
{
stringstream temp;
temp << setw(4) << i;

str = temp.str();
}
string str;
FormatString(10, str);
cout << str << endl;

特点很鲜明:易用性不如sprintf,使用了额外的缓冲区stringstream。但是现在没有了缓冲区溢出和类型安全的担心,而且我们可以做个更通用的版本
template<typename T>
void FormatString(T value, string &str)
{
stringstream temp;
temp << setw(4) << value; // 现在可以接受任意类型的数据

str = temp.str();
}
 

四、std::strstream
哎,这个要被抛弃了,成为标准的牺牲品!还是给他点信心吧
template<typename T>
void FormatString(T value, char *pBuf, int nBufLen)
{
strstream temp(pBuf, nBufLen); // 传递指定缓冲区及其大小
temp << setw(4) << value << ends;
}

不错的表现啊(相对stringstream讲,无需额外的内存),但是要被标准抛弃~
 
五、str2::lexical_cast / boost::lexical_cast
cout << boost::lexical_cast(10) << endl;

很简单,实现也是基于stringstream的,而其快进入C++标准了,不需多言。
 
六、boost::format
cout << format("%1% %2%") % 36 % 77  << endl; // 输出 36 77

用法与srpintf类似,不过看上去N不像~不喜欢!
 
 
效率比试:
VS2008 Release模式
DWORD dwLast = 0;
volatile DWORD count = 1000000;

char buf[64] = {0};

{
CCYPerformance test(dwLast);
for(DWORD i = 0; i != count; ++i)
{
FormatString(0xFFFF, buf);
}
}
cout << "sprintf:" << dwLast << endl;

dwLast = 0;
{
CCYPerformance test(dwLast);
for(DWORD i = 0; i != count; ++i)
{
FormatNString(0xFFFF, buf, 64);
}
}
cout << "snprintf:" << dwLast << endl;

dwLast = 0;
string str;
{
CCYPerformance test(dwLast);
for(DWORD i = 0; i != count; ++i)
{
FormatString(0xFFFF, str);
}
}
cout << "stringstream:" << dwLast << endl;

dwLast = 0;
{
CCYPerformance test(dwLast);
for(DWORD i = 0; i != count; ++i)
{
FormatString(0xFFFF, buf, 64);
}
}
cout << "strstream:" << dwLast << endl;


dwLast = 0;
{
CCYPerformance test(dwLast);
for(DWORD i = 0; i != count; ++i)
{
FormatBoostString(0xFFFF, str);
}
}
cout << "boost::lexical_cast:" << dwLast << endl;

Test

 

很出乎我得意料~Boost这么快啊~佩服佩服!不愧是大师的佳作!

 

总结:

 默认情况下,效率不是关键如果效率是瓶颈如果只想做字符转换boost::lexical_castboost::lexical_cast/snprintf简单的格式化(UNICODE/ASCII)stringstream/strstreamsnprintf较为负载的格式化stringstream/snprintfsnprintf
原创粉丝点击