我必须用sprintf格式化std::string,并将其发送到文件流。我该怎么做呢?


当前回答

这是一个特定于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 );

其他回答

我知道这个问题已经被回答过很多次了,但下面这个更简洁:

std::string format(const std::string fmt_str, ...)
{
    va_list ap;
    char *fp = NULL;
    va_start(ap, fmt_str);
    vasprintf(&fp, fmt_str.c_str(), ap);
    va_end(ap);
    std::unique_ptr<char[]> formatted(fp);
    return std::string(formatted.get());
}

例子:

#include <iostream>
#include <random>

int main()
{
    std::random_device r;
    std::cout << format("Hello %d!\n", r());
}

参见http://rextester.com/NJB14150

到目前为止,所有的答案似乎都有一个或多个这样的问题:(1)它可能无法在vc++上工作(2)它需要额外的依赖,如boost或fmt(3)它太复杂的自定义实现,可能没有经过很好的测试。

下面的代码解决了上述所有问题。

#include <string>
#include <cstdarg>
#include <memory>

std::string stringf(const char* format, ...)
{
    va_list args;
    va_start(args, format);
    #ifndef _MSC_VER

        //GCC generates warning for valid use of snprintf to get
        //size of result string. We suppress warning with below macro.
        #ifdef __GNUC__
        #pragma GCC diagnostic push
        #pragma GCC diagnostic ignored "-Wformat-nonliteral"
        #endif

        size_t size = std::snprintf(nullptr, 0, format, args) + 1; // Extra space for '\0'

        #ifdef __GNUC__
        # pragma GCC diagnostic pop
        #endif

        std::unique_ptr<char[]> buf(new char[ size ] ); 
        std::vsnprintf(buf.get(), size, format, args);
        return std::string(buf.get(), buf.get() + size - 1 ); // We don't want the '\0' inside
    #else
        int size = _vscprintf(format, args);
        std::string result(++size, 0);
        vsnprintf_s((char*)result.data(), size, _TRUNCATE, format, args);
        return result;
    #endif
    va_end(args);
}    

int main() {
    float f = 3.f;
    int i = 5;
    std::string s = "hello!";
    auto rs = stringf("i=%d, f=%f, s=%s", i, f, s.c_str());
    printf("%s", rs.c_str());
    return 0;
}

注:

Separate VC++ code branch is necessary because VC++ has decided to deprecate snprintf which will generate compiler warnings for other highly voted answers above. As I always run in "warnings as errors" mode, its no go for me. The function accepts char * instead of std::string. This because most of the time this function would be called with literal string which is indeed char *, not std::string. In case you do have std::string as format parameter, then just call .c_str(). Name of the function is stringf instead of things like string_format to keepup with printf, scanf etc. It doesn't address safety issue (i.e. bad parameters can potentially cause seg fault instead of exception). If you need this then you are better off with boost or fmt libraries. My preference here would be fmt because it is just one header and source file to drop in the project while having less weird formatting syntax than boost. However both are non-compatible with printf format strings so below is still useful in that case. The stringf code passes through GCC strict mode compilation. This requires extra #pragma macros to suppress false positives in GCC warnings.

以上代码已在,

GCC 4.9.2 11 / c++ / C + + 14 vc++编译器19.0 铿锵声3.7.0

这是一个特定于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 );

这个问题已经解决了。但是,我认为这是c++中格式化字符串的另一种方式

class string_format {
private:
    std::string _result;
public:
    string_format( ) { }
    ~string_format( ) { std::string( ).swap( _result ); }
    const std::string& get_data( ) const { return _result; }
    template<typename T, typename... Targs>
    void format( const char* fmt, T value, Targs... Fargs ) {
        for ( ; *fmt != '\0'; fmt++ ) {
            if ( *fmt == '%' ) {
                _result += value;
                this->format( fmt + 1, Fargs..., 0 ); // recursive call
                return;
            }
            _result += *fmt;
        }
    }
    friend std::ostream& operator<<( std::ostream& ostream, const string_format& inst );
};
inline std::string& operator+=( std::string& str, int val ) {
    str.append( std::to_string( val ) );
    return str;
}
inline std::string& operator+=( std::string& str, double val ) {
    str.append( std::to_string( val ) );
    return str;
}
inline std::string& operator+=( std::string& str, bool val ) {
    str.append( val ? "true" : "false" );
    return str;
}
inline std::ostream& operator<<( std::ostream& ostream, const string_format& inst ) {
    ostream << inst.get_data( );
    return ostream;
}

并测试这个类:

string_format fmt;
fmt.format( "Hello % and is working ? Ans: %", "world", true );
std::cout << fmt;

你可以在这里查一下

我通常用这个:

std::string myformat(const char *const fmt, ...)
{
        char *buffer = NULL;
        va_list ap;

        va_start(ap, fmt);
        (void)vasprintf(&buffer, fmt, ap);
        va_end(ap);

        std::string result = buffer;
        free(buffer);

        return result;
}

缺点:并非所有系统都支持vasprint