std::string formatting like sprintf

2018-12-31 14:19发布

I have to format std::string with sprintf and send it into file stream. How can I do this?

30条回答
情到深处是孤独
2楼-- · 2018-12-31 14:42

I wrote my own using vsnprintf so it returns string instead of having to create my own buffer.

#include <string>
#include <cstdarg>

//missing string printf
//this is safe and convenient but not exactly efficient
inline std::string format(const char* fmt, ...){
    int size = 512;
    char* buffer = 0;
    buffer = new char[size];
    va_list vl;
    va_start(vl, fmt);
    int nsize = vsnprintf(buffer, size, fmt, vl);
    if(size<=nsize){ //fail delete buffer and try again
        delete[] buffer;
        buffer = 0;
        buffer = new char[nsize+1]; //+1 for /0
        nsize = vsnprintf(buffer, size, fmt, vl);
    }
    std::string ret(buffer);
    va_end(vl);
    delete[] buffer;
    return ret;
}

So you can use it like

std::string mystr = format("%s %d %10.5f", "omg", 1, 10.5);
查看更多
余欢
3楼-- · 2018-12-31 14:42

You could try this:

string str;
str.resize( _MAX_PATH );

sprintf( &str[0], "%s %s", "hello", "world" );
// optionals
// sprintf_s( &str[0], str.length(), "%s %s", "hello", "world" ); // Microsoft
// #include <stdio.h>
// snprintf( &str[0], str.length(), "%s %s", "hello", "world" ); // c++11

str.resize( strlen( str.data() ) + 1 );
查看更多
公子世无双
4楼-- · 2018-12-31 14:42
_return.desc = (boost::format("fail to detect. cv_result = %d") % st_result).str();
查看更多
柔情千种
5楼-- · 2018-12-31 14:44

Unfortunately, most of the answers here use varargs which are inherently unsafe unless you use something like GCC's format attribute which only works with literal format strings. You can see why these functions are unsafe on the following example:

std::string format_str = "%s";
string_format(format_str, format_str[0]);

where string_format is an implementation from the Erik Aronesty's answer. This code compiles, but it will most likely crash when you try to run it:

$ g++ -Wall -Wextra -pedantic test.cc 
$ ./a.out 
Segmentation fault: 11

It is possible to implement a safe printf and extend it to format std::string using (variadic) templates. This has been done in the {fmt} library, which provides a safe alternative to sprintf returning std::string:

std::string format_str = "The answer is %d";
std::string result = fmt::sprintf(format_str, 42);

{fmt} keeps track of the argument types and if the type doesn't match format specification there is no segmentation fault, just an exception or a compile-time error if constexpr format string checks are used.

Disclaimer: I'm the author of {fmt}.

查看更多
浪荡孟婆
6楼-- · 2018-12-31 14:45

I usually use this:

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;
}

Disadvantage: not all systems support vasprint

查看更多
与君花间醉酒
7楼-- · 2018-12-31 14:45

Below slightly modified version of @iFreilicht answer, updated to C++14 (usage of make_unique function instead of raw declaration) and added support for std::string arguments (based on Kenny Kerr article)

#include <iostream>
#include <memory>
#include <string>
#include <cstdio>

template <typename T>
T process_arg(T value) noexcept
{
    return value;
}

template <typename T>
T const * process_arg(std::basic_string<T> const & value) noexcept
{
    return value.c_str();
}

template<typename ... Args>
std::string string_format(const std::string& format, Args const & ... args)
{
    const auto fmt = format.c_str();
    const size_t size = std::snprintf(nullptr, 0, fmt, process_arg(args) ...) + 1;
    auto buf = std::make_unique<char[]>(size);
    std::snprintf(buf.get(), size, fmt, process_arg(args) ...);
    auto res = std::string(buf.get(), buf.get() + size - 1);
    return res;
}

int main()
{
    int i = 3;
    float f = 5.f;
    char* s0 = "hello";
    std::string s1 = "world";
    std::cout << string_format("i=%d, f=%f, s=%s %s", i, f, s0, s1) << "\n";
}

Output:

i = 3, f = 5.000000, s = hello world

Feel free to merge this answer with the original one if desired.

查看更多
登录 后发表回答