dre*_*lax 2 c++ string printf c++-standard-library
我对C++比较陌生,我仍然在掌握C++标准库.为了帮助从C转换,我想格式化一个std::string使用printf风格的格式化程序.我意识到这stringstream是一种更加类型安全的方法,但我发现自己发现printf风格更容易阅读和处理(至少,暂时).这是我的功能:
using namespace std;
string formatStdString(const string &format, ...)
{
va_list va;
string output;
size_t needed;
size_t used;
va_start(va, format);
needed = vsnprintf(&output[0], 0, format.c_str(), va);
output.resize(needed + 1); // for null terminator??
va_end(va);
va_start(va, format);
used = vsnprintf(&output[0], output.capacity(), format.c_str(), va);
// assert(used == needed);
va_end(va);
return output;
}Run Code Online (Sandbox Code Playgroud)
这很有效.我不确定的一些事情是:
capacity()在这里打电话是正确的功能吗?我一直在想length(),因为字符串中的第一个字符是a,所以会返回0 '\0'.偶尔在将这个字符串的内容写入套接字(使用它的c_str()和length())时,我在接收端弹出了空字节,这引起了一些悲伤,但它们看起来似乎不一致.如果我根本不使用此函数,则不会出现空字节.
Dav*_*eas 12
使用当前标准(此处的升级标准不同),不能保证由其管理的内部存储器缓冲区std::string将是连续的,或者该.c_str()方法返回指向内部数据表示的指针(允许实现生成连续的读取 -只有该操作的块并返回一个指针.指向实际内部数据的指针可以使用.data()成员方法检索,但请注意它也返回一个常量指针:即它不是为了你修改内容.由.data()它返回的缓冲区不一定是null终止的,实现只需要在c_str()调用时保证null终止,所以即使在实现where .data()和.c_str()被调用时,实现可以在\0调用后者时将其添加到缓冲区的末尾.
标准旨在允许绳索实现,所以原则上做你正在尝试的东西是不安全的,并且从标准的角度来看你应该使用中间std::vector(保证连续性,并且有一个保证&myvector[0]是指向第一个分配的实际缓冲区块).
在我所知道的所有实现中,处理的内部存储器std::string实际上是一个连续的缓冲区,并且使用.data()未定义的行为(写入常量变量),但即使它不正确也可以工作(我会避免它).您应该使用为此目的而设计的其他库,例如boost::format.
关于null终止.如果你最终决定遵循未定义的路径......你需要为null终止符分配额外的空间,因为库会将它写入缓冲区.现在,问题在于,与C风格的字符串不同,std::strings可以在内部保存空指针,因此您必须向下调整字符串的大小以适应从包含否的开头的最大连续内存块\0.这可能是您发现虚假空字符的问题.这意味着vsnprintf必须遵循使用(或族)的不良方法,str.resize( strlen( str.c_str() ) )在第一个之后丢弃字符串的所有内容\0.
总的来说,我会反对这种方法,并坚持要么习惯了C++的格式化方式,使用第三方库(boost是第三方,但它也是最标准的非标准库),使用向量或管理内存就像在C ......但最后的选择应该像瘟疫一样避免.
// A safe way in C++ of using vsnprintf:
std::vector<char> tmp( 1000 ); // expected maximum size
vsnprintf( &tmp[0], tmp.size(), "Hi %s", name.c_str() ); // assuming name to be a string
std::string salute( &tmp[0] );
Run Code Online (Sandbox Code Playgroud)
boost::format如果您喜欢使用printf()流,请使用.
编辑:为了说清楚,实际上我完全赞同艾伦,他说你应该使用流.