我必须用sprintf格式化std::string,并将其发送到文件流。我该怎么做呢?
当前回答
_return.desc = (boost::format("fail to detect. cv_result = %d") % st_result).str();
其他回答
这是一个特定于Windows的解决方案,旨在避免Visual Studio中的编译器警告而不消除它们。所讨论的警告是针对使用std::string和va_start,这会错误地产生警告,以及针对使用已弃用的printf变量。
template<typename ... va>
std::string Format( const std::string& format, va ... args )
{
std::string s;
s.resize( _scprintf( format.c_str(), args ... ) + 1 );
s.resize( _snprintf_s( s.data(), s.capacity(), _TRUNCATE, format.c_str(), args ... ) );
return s;
}
template<typename ... va>
std::wstring Format( const std::wstring& format, va ... args )
{
std::wstring s;
s.resize( _scwprintf( format.c_str(), args ... ) + 1 );
s.resize( _snwprintf_s( s.data(), s.capacity(), _TRUNCATE, format.c_str(), args ... ) );
return s;
}
std::string s = Format( "%hs %d", "abc", 123 );
std::wstring ws = Format( L"%hs %d", "abc", 123 );
[编辑:20/05/25]更好的是… 在标题:
// `say` prints the values
// `says` returns a string instead of printing
// `sayss` appends the values to it's first argument instead of printing
// `sayerr` prints the values and returns `false` (useful for return statement fail-report)<br/>
void PRINTSTRING(const std::string &s); //cater for GUI, terminal, whatever..
template<typename...P> void say(P...p) { std::string r{}; std::stringstream ss(""); (ss<<...<<p); r=ss.str(); PRINTSTRING(r); }
template<typename...P> std::string says(P...p) { std::string r{}; std::stringstream ss(""); (ss<<...<<p); r=ss.str(); return r; }
template<typename...P> void sayss(std::string &s, P...p) { std::string r{}; std::stringstream ss(""); (ss<<...<<p); r=ss.str(); s+=r; } //APPENDS! to s!
template<typename...P> bool sayerr(P...p) { std::string r{}; std::stringstream ss("ERROR: "); (ss<<...<<p); r=ss.str(); PRINTSTRING(r); return false; }
PRINTSTRING(r)-函数是为了满足GUI或终端或任何特殊输出需要使用#ifdef _some_flag_,默认是:
void PRINTSTRING(const std::string &s) { std::cout << s << std::flush; }
[edit '17/8/31]添加可变参数模板版本'vtspf(..)':
template<typename T> const std::string type_to_string(const T &v)
{
std::ostringstream ss;
ss << v;
return ss.str();
};
template<typename T> const T string_to_type(const std::string &str)
{
std::istringstream ss(str);
T ret;
ss >> ret;
return ret;
};
template<typename...P> void vtspf_priv(std::string &s) {}
template<typename H, typename...P> void vtspf_priv(std::string &s, H h, P...p)
{
s+=type_to_string(h);
vtspf_priv(s, p...);
}
template<typename...P> std::string temp_vtspf(P...p)
{
std::string s("");
vtspf_priv(s, p...);
return s;
}
它实际上是一个逗号分隔的版本(而不是)有时阻碍<<-操作符,像这样使用:
char chSpace=' ';
double pi=3.1415;
std::string sWorld="World", str_var;
str_var = vtspf("Hello", ',', chSpace, sWorld, ", pi=", pi);
[编辑]在Erik Aronesty的回答中使用了这个技巧(上图):
#include <string>
#include <cstdarg>
#include <cstdio>
//=============================================================================
void spf(std::string &s, const std::string fmt, ...)
{
int n, size=100;
bool b=false;
va_list marker;
while (!b)
{
s.resize(size);
va_start(marker, fmt);
n = vsnprintf((char*)s.c_str(), size, fmt.c_str(), marker);
va_end(marker);
if ((n>0) && ((b=(n<size))==true)) s.resize(n); else size*=2;
}
}
//=============================================================================
void spfa(std::string &s, const std::string fmt, ...)
{
std::string ss;
int n, size=100;
bool b=false;
va_list marker;
while (!b)
{
ss.resize(size);
va_start(marker, fmt);
n = vsnprintf((char*)ss.c_str(), size, fmt.c_str(), marker);
va_end(marker);
if ((n>0) && ((b=(n<size))==true)) ss.resize(n); else size*=2;
}
s += ss;
}
(之前的回答) 这是一个很晚的回答,但对于那些像我一样喜欢“sprintf”方式的人来说:我已经编写并正在使用以下函数。如果你喜欢它,你可以扩展%-选项以更接近sprintf选项;现在里面的已经足够我用了。 使用stringf()和stringfappend()与使用sprintf相同。只要记住参数…必须是POD类型。
//=============================================================================
void DoFormatting(std::string& sF, const char* sformat, va_list marker)
{
char *s, ch=0;
int n, i=0, m;
long l;
double d;
std::string sf = sformat;
std::stringstream ss;
m = sf.length();
while (i<m)
{
ch = sf.at(i);
if (ch == '%')
{
i++;
if (i<m)
{
ch = sf.at(i);
switch(ch)
{
case 's': { s = va_arg(marker, char*); ss << s; } break;
case 'c': { n = va_arg(marker, int); ss << (char)n; } break;
case 'd': { n = va_arg(marker, int); ss << (int)n; } break;
case 'l': { l = va_arg(marker, long); ss << (long)l; } break;
case 'f': { d = va_arg(marker, double); ss << (float)d; } break;
case 'e': { d = va_arg(marker, double); ss << (double)d; } break;
case 'X':
case 'x':
{
if (++i<m)
{
ss << std::hex << std::setiosflags (std::ios_base::showbase);
if (ch == 'X') ss << std::setiosflags (std::ios_base::uppercase);
char ch2 = sf.at(i);
if (ch2 == 'c') { n = va_arg(marker, int); ss << std::hex << (char)n; }
else if (ch2 == 'd') { n = va_arg(marker, int); ss << std::hex << (int)n; }
else if (ch2 == 'l') { l = va_arg(marker, long); ss << std::hex << (long)l; }
else ss << '%' << ch << ch2;
ss << std::resetiosflags (std::ios_base::showbase | std::ios_base::uppercase) << std::dec;
}
} break;
case '%': { ss << '%'; } break;
default:
{
ss << "%" << ch;
//i = m; //get out of loop
}
}
}
}
else ss << ch;
i++;
}
va_end(marker);
sF = ss.str();
}
//=============================================================================
void stringf(string& stgt,const char *sformat, ... )
{
va_list marker;
va_start(marker, sformat);
DoFormatting(stgt, sformat, marker);
}
//=============================================================================
void stringfappend(string& stgt,const char *sformat, ... )
{
string sF = "";
va_list marker;
va_start(marker, sformat);
DoFormatting(sF, sformat, marker);
stgt += sF;
}
我现在将为Visual Studio编写版本,希望有一天有人会让它变得可移植。(怀疑需要用vsnwprintf替换_vsnwprintf之类的东西。)
您需要使用项目配置中的define _CRT_SECURE_NO_WARNINGS来禁用已弃用的警告。
我使用_vsnwprintf与第一个参数作为nullptr能够估计缓冲区大小,保留wstring缓冲区,然后格式化字符串直接到缓冲区。
不确定为什么需要禁用已弃用警告,因为相同方法调用(_vsnwprintf_s)的安全版本不能使用nullptr作为输入。怀疑需要报告给微软c++团队。
这个版本应该同时使用- string类或wstring类。
如果你发现任何错误或不一致,请再问一次,我会尽力修复它。
stringHelpers.h:
#pragma once
#include <string>
//
// Formats string/wstring according to format, if formatting fails (e.g. invalid %s pointer - returns empty string)
//
template <typename T>
std::basic_string<T> sFormat(const T* format, ...)
{
va_list args;
va_start(args, format);
int size;
if constexpr (std::is_same_v<T, char>)
size = vsnprintf(nullptr, 0, format, args);
else
size = _vsnwprintf(nullptr, 0, format, args);
size++; // Zero termination
std::basic_string<T> s;
s.resize(size);
if constexpr (std::is_same_v<T, char>)
vsnprintf(&s[0], size, format, args);
else
_vsnwprintf(&s[0], size, format, args);
va_end(args);
return s;
}
以上是代码示例,可以复制。我将维护工作版本在我自己的仓库在github:
https://github.com/tapika/cppscriptcore/blob/master/SolutionProjectModel/helpers.h#L12
Boost::format()提供了你想要的功能:
Boost格式库简介如下:
format对象由format-string构造,然后通过反复调用运算符%来给出参数。 然后,每个参数都被转换为字符串,这些字符串又根据format-string组合成一个字符串。
#include <boost/format.hpp>
cout << boost::format("writing %1%, x=%2% : %3%-th try") % "toto" % 40.23 % 50;
// prints "writing toto, x=40.230 : 50-th try"
从Dacav和pixelpoint的答案中获得灵感。我玩了一下,得到了这个:
#include <cstdarg>
#include <cstdio>
#include <string>
std::string format(const char* fmt, ...)
{
va_list vl;
va_start(vl, fmt);
int size = vsnprintf(0, 0, fmt, vl) + sizeof('\0');
va_end(vl);
char buffer[size];
va_start(vl, fmt);
size = vsnprintf(buffer, size, fmt, vl);
va_end(vl);
return std::string(buffer, size);
}
通过合理的编程实践,我相信代码应该足够了,但是我仍然对更安全的替代方案持开放态度,这些替代方案仍然足够简单,不需要c++ 11。
下面是另一个版本,它使用初始缓冲区来防止在初始缓冲区已经足够多时再次调用vsnprintf()。
std::string format(const char* fmt, ...)
{
va_list vl;
int size;
enum { INITIAL_BUFFER_SIZE = 512 };
{
char buffer[INITIAL_BUFFER_SIZE];
va_start(vl, fmt);
size = vsnprintf(buffer, INITIAL_BUFFER_SIZE, fmt, vl);
va_end(vl);
if (size < INITIAL_BUFFER_SIZE)
return std::string(buffer, size);
}
size += sizeof('\0');
char buffer[size];
va_start(vl, fmt);
size = vsnprintf(buffer, size, fmt, vl);
va_end(vl);
return std::string(buffer, size);
}
(事实证明,这个版本与Piti Ongmongkolkul的答案相似,只是它没有使用new和delete[],并且在创建std::string时指定了大小。
The idea here of not using new and delete[] is to imply usage of the stack over the heap since it doesn't need to call allocation and deallocation functions, however if not properly used, it could be dangerous to buffer overflows in some (perhaps old, or perhaps just vulnerable) systems. If this is a concern, I highly suggest using new and delete[] instead. Note that the only concern here is about the allocations as vsnprintf() is already called with limits, so specifying a limit based on the size allocated on the second buffer would also prevent those.)
推荐文章
- decltype(auto)的一些用途是什么?
- Shared_ptr转换为数组:应该使用它吗?
- Printf与std::字符串?
- 禁用复制构造函数
- 不区分大小写的“in”
- 我如何得到一个字符串的前n个字符而不检查大小或出界?
- 只接受特定类型的c++模板
- 如何在PHP中截断字符串最接近于一定数量的字符?
- c#和Java中的泛型有什么不同?和模板在c++ ?
- Ruby数组到字符串的转换
- c++ 11中的递归lambda函数
- 在c++中指针使用NULL或0(零)吗?
- 为什么在Java和。net中不能修改字符串?
- 在c++中,如何将int值附加到字符串中?
- 如何创建一个日期对象从字符串在javascript