std:: форматирование строк как sprintf

Я должен отформатировать std::string С sprintf и отправить его в файловый поток. Как я могу это сделать?

30 ответов


вы не можете сделать это напрямую, потому что у вас нет доступа на запись к базовому буферу (до c++11; см.комментарий). Вам нужно будет сделать это сначала в C-строке, а затем скопировать ее в std:: string:

  char buff[100];
  snprintf(buff, sizeof(buff), "%s", "Hello");
  std::string buffAsStdStr = buff;

но я не уверен, почему бы вам просто не использовать поток строк? Я предполагаю, что у вас есть конкретные причины не делать этого:

  std::ostringstream stringStream;
  stringStream << "Hello";
  std::string copyOfStr = stringStream.str();

в C++11 решение, которое использует vsnprintf() внутри:

#include <stdarg.h>  // For va_start, etc.

std::string string_format(const std::string fmt, ...) {
    int size = ((int)fmt.size()) * 2 + 50;   // Use a rubric appropriate for your code
    std::string str;
    va_list ap;
    while (1) {     // Maximum two passes on a POSIX system...
        str.resize(size);
        va_start(ap, fmt);
        int n = vsnprintf((char *)str.data(), size, fmt.c_str(), ap);
        va_end(ap);
        if (n > -1 && n < size) {  // Everything worked
            str.resize(n);
            return str;
        }
        if (n > -1)  // Needed size returned
            size = n + 1;   // For null char
        else
            size *= 2;      // Guess at a larger size (OS specific)
    }
    return str;
}

более безопасный и эффективный (я протестировал его, и это быстрее) подход:

#include <stdarg.h>  // For va_start, etc.
#include <memory>    // For std::unique_ptr

std::string string_format(const std::string fmt_str, ...) {
    int final_n, n = ((int)fmt_str.size()) * 2; /* Reserve two times as much as the length of the fmt_str */
    std::unique_ptr<char[]> formatted;
    va_list ap;
    while(1) {
        formatted.reset(new char[n]); /* Wrap the plain char array into the unique_ptr */
        strcpy(&formatted[0], fmt_str.c_str());
        va_start(ap, fmt_str);
        final_n = vsnprintf(&formatted[0], n, fmt_str.c_str(), ap);
        va_end(ap);
        if (final_n < 0 || final_n >= n)
            n += abs(final_n - n + 1);
        else
            break;
    }
    return std::string(formatted.get());
}

на fmt_str передается по значению, чтобы соответствовать требованиям va_start.

Примечание:" более безопасная "и" более быстрая " версия не работает на некоторых системах. Следовательно, оба они все еще перечислены. Кроме того," быстрее " полностью зависит от правильного шага предварительного распределения, иначе strcpy выводит его медленнее.


используя C++11 std::snprintf, это становится довольно простой и безопасной задачей. Я вижу много ответов на этот вопрос, которые, по-видимому, были написаны до времени C++11, которые используют фиксированные длины буферов и vargs, что я бы не рекомендовал по соображениям безопасности, эффективности и ясности.

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

using namespace std; //Don't if you're in a header-file

template<typename ... Args>
string string_format( const std::string& format, Args ... args )
{
    size_t size = snprintf( nullptr, 0, format.c_str(), args ... ) + 1; // Extra space for ''
    unique_ptr<char[]> buf( new char[ size ] ); 
    snprintf( buf.get(), size, format.c_str(), args ... );
    return string( buf.get(), buf.get() + size - 1 ); // We don't want the '' inside
}

приведенный выше фрагмент кода лицензируется в разделе Лицензия CC0 1.0.

построчно объяснение:

цель: пишите на char* С помощью std::snprintf а затем преобразовать это в std::string.

во-первых, мы определяем желаемую длину массива.

от cppreference.com:

возвращаемое значение

[...] Если результирующая строка усекается из-за ограничения buf_size, функция возвращает общее количество символов (не в том числе завершающий нулевой байт), который был бы написан, если бы предел был не навязывать.

это означает, что желаемый размер - это количество символов плюс один, так что нуль-Терминатор будет сидеть после всех других символов и что он может быть снова отрезан строковым конструктором. Эта проблема была объяснена @alexk7 в комментариях.

затем мы выделяем новый массив символов и назначаем его std::unique_ptr. Это вообще советую, так как вам не придется вручную delete это снова.

обратите внимание, что это не безопасный способ выделить unique_ptr с пользовательскими типами, поскольку вы не можете освободить память, если конструктор выдает исключение!

после этого, мы можем, конечно, просто использовать snprintf для его целевого использования и записи форматированной строки в char[] а затем создать и вернуть новый std::string от этого.


вы можете увидеть пример в действии здесь.


если вы хотите использовать std::string в списке аргументов, взгляните на в этом суть.


дополнительная информация Visual Studio потребители:

как поясняется в ответ, Microsoft переименовала std::snprintf to _snprintf (да, без std::). MS далее устанавливает его как устаревший и советует использовать _snprintf_s вместо этого, однако _snprintf_s не будет принимать буфер равным нулю или меньше форматированного вывода и не будет вычислять длину выходных данных, если это произойдет. Поэтому, чтобы избавиться от предупреждений об устаревании во время компиляции, вы можете вставить следующую строку в верхней части файла, который содержит использование _snprintf:

#pragma warning(disable : 4996)

boost::format() обеспечивает функциональность, которую вы хотите:

Как из библиотеки формата Boost synopsis:

объект format строится из 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"

к сожалению, большинство ответов здесь используют varargs, которые по своей сути небезопасны, если вы не используете что-то вроде GCC format атрибут, который работает только со строками литерального формата. Вы можете увидеть, почему эти функции небезопасны в следующем примере:

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

здесь string_format является реализацией из ответа Эрика Аронести. Этот код компилируется, но, скорее всего, произойдет сбой при попытке его запуска:

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

возможно снабдить сейф printf и расширить его до формата std::string использование шаблонов (variadic). Это было сделано в библиотека fmt, который обеспечивает безопасную альтернативу sprintf возвращение std::string:

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

fmt отслеживает типы аргументов, и если тип не соответствует спецификации формата, нет ошибки сегментации, просто исключение.

отказ от ответственности: я автор этой библиотеки.


Если вы хотите только синтаксис, подобный printf (без вызова printf самостоятельно), посмотрите на Увеличить Формат.


Я написал свой собственный, используя vsnprintf, поэтому он возвращает строку вместо того, чтобы создавать собственный буфер.

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

Так что вы можете использовать его как

std::string mystr = format("%s %d %10.5f", "omg", 1, 10.5);

[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);


[edit] адаптировано для использования техники в ответе Эрика Аронести (выше):

#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" - путь: я написаны и используются следующие функции. Если вам это нравится, вы можете расширить %-options, чтобы более точно соответствовать 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;
}

для того, чтобы формат std::string в "sprintf" манере, вызов snprintf (аргументы nullptr и 0), чтобы получить необходимую длину буфера. Напишите свою функцию с помощью вариационного шаблона C++11 следующим образом:

#include <cstdio>
#include <string>
#include <cassert>

template< typename... Args >
std::string string_sprintf( const char* format, Args... args ) {
  int length = std::snprintf( nullptr, 0, format, args... );
  assert( length >= 0 );

  char* buf = new char[length + 1];
  std::snprintf( buf, length + 1, format, args... );

  std::string str( buf );
  delete[] buf;
  return std::move(str);
}

компиляция с поддержкой C++11, например в GCC:g++ -std=c++11

использование:

  std::cout << string_sprintf("%g, %g\n", 1.23, 0.001);

вот как это делает google:StringPrintf (лицензия BSD)
и facebook делает это довольно похожим образом:StringPrintf (Лицензия Apache)
Оба обеспечивают удобный StringAppendF тоже.


мои пять копеек на этот очень популярный вопрос.

цитировать на странице справочника о printfфункции:

после успешного возврата эти функции возвращают количество напечатанных символов (за исключением нулевого байта, используемого для завершения вывода в строки).

функции snprintf () и vsnprintf () не записывают больше байтов размера (включая завершающий нулевой байт ('\0')). Если выход был усечен из-за это ограничение, то возвращаемое значение-это количество символов (исключая завершающий нулевой байт), которые были бы записаны в конечную строку, если бы было достаточно места. Таким образом, возвращаемое значение size или more означает, что вывод был усечен.

другими словами, разумная реализация C++11 должна быть следующей:

#include <string>
#include <cstdio>

template <typename... Ts>
std::string fmt (const std::string &fmt, Ts... vs)
{
    char b;
    size_t required = std::snprintf(&b, 0, fmt.c_str(), vs...) + 1;
        // See comments: the +1 is necessary, while the first parameter
        //               can also be set to nullptr

    char bytes[required];
    std::snprintf(bytes, required, fmt.c_str(), vs...);

    return std::string(bytes);
}

он работает довольно хорошо:)

Шаблоны Variadic поддерживаются только в C++11. Ответ из pixelpoint показать аналогичную технику, используя старые стили программирования.

странно, что C++ не имеет такой вещи из коробки. Они недавно добавили to_string(), который на мой взгляд является большим шагом вперед. Мне интересно, добавят ли они .format оператором std::string в конце концов...

редактировать

как отметил alexk7, A +1 необходимо для возвращаемого значения std::snprintf, так как нам нужно место для байт. Интуитивно, на большинстве архитектур отсутствует +1 вызывает required целое число, которое будет частично перезаписано с помощью 0. Это произойдет после оценка required как фактический параметр для std::snprintf, поэтому эффект не должен быть видимым.

однако эта проблема может измениться, например, при оптимизации компилятора: что делать, если компилятор решит использовать регистр для required переменной? Это ошибки, которые иногда приводят в вопросах безопасности.


template<typename... Args>
std::string string_format(const char* fmt, Args... args)
{
    size_t size = snprintf(nullptr, 0, fmt, args...);
    std::string buf;
    buf.reserve(size + 1);
    buf.resize(size);
    snprintf(&buf[0], size + 1, fmt, args...);
    return buf;
}

использование C99 snprintf и C++11


на основе ответа, предоставленного Эриком Аронести:

std::string string_format(const std::string &fmt, ...) {
    std::vector<char> str(100,'');
    va_list ap;
    while (1) {
        va_start(ap, fmt);
        auto n = vsnprintf(str.data(), str.size(), fmt.c_str(), ap);
        va_end(ap);
        if ((n > -1) && (size_t(n) < str.size())) {
            return str.data();
        }
        if (n > -1)
            str.resize( n + 1 );
        else
            str.resize( str.size() * 2);
    }
    return str.data();
}

это позволяет избежать необходимости отбрасывать const с результатом .c_str() что было в исходном ответе.


inline void format(string& a_string, const char* fmt, ...)
{
    va_list vl;
    va_start(vl, fmt);
    int size = _vscprintf( fmt, vl );
    a_string.resize( ++size );
    vsnprintf_s((char*)a_string.data(), size, _TRUNCATE, fmt, vl);
    va_end(vl);
}

string не имеет того, что вам нужно, но std::stringstream делает. Используйте stringstream для создания строки, а затем извлеките строку. здесь Это полный список того, что вы можете сделать. Например:

cout.setprecision(10); //stringstream is a stream like cout

даст вам 10 десятичных знаков точности при печати двойного или float.


вы можете попробовать это:

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

Это код, который я использую для этого в своей программе... Ничего особенного,но это помогает... Обратите внимание, что вам придется настроить свой размер, как это применимо. MAX_BUFFER для меня-1024.

std::string Format ( const char *fmt, ... )
{
    char textString[MAX_BUFFER*5] = {''};

    // -- Empty the buffer properly to ensure no leaks.
    memset(textString, '', sizeof(textString));

    va_list args;
    va_start ( args, fmt );
    vsnprintf ( textString, MAX_BUFFER*5, fmt, args );
    va_end ( args );
    std::string retStr = textString;
    return retStr;
}

взял идею из 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('');
    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('');

    char buffer[size];

    va_start(vl, fmt);
    size = vsnprintf(buffer, size, fmt, vl);
    va_end(vl);

    return std::string(buffer, size);
}

(оказывается, эта версия просто похожа на ответ Пити Онгмонгколкула, только то, что он не использует new и delete[], а также указывает размер при создании std::string.

идея здесь не использовать new и delete[] подразумевает использование стека над кучей, так как ему не нужно вызывать функции распределения и освобождения, однако, если он не используется должным образом, он может быть опасно буферизировать переполнения в некоторых (возможно, старых или, возможно, просто уязвимых) системах. Если это вызывает беспокойство, я настоятельно рекомендую использовать new и . Обратите внимание, что единственная проблема здесь заключается в распределении как vsnprintf() уже вызывается с ограничениями, поэтому указание предела на основе размера, выделенного на втором буфере, также предотвратит их.)


Я обычно использую этот:

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


Если вы находитесь в системе, которая имеет asprintf(3), вы можете легко обернуть его:

#include <iostream>
#include <cstdarg>
#include <cstdio>

std::string format(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));

std::string format(const char *fmt, ...)
{
    std::string result;

    va_list ap;
    va_start(ap, fmt);

    char *tmp = 0;
    int res = vasprintf(&tmp, fmt, ap);
    va_end(ap);

    if (res != -1) {
        result = tmp;
        free(tmp);
    } else {
        // The vasprintf call failed, either do nothing and
        // fall through (will return empty string) or
        // throw an exception, if your code uses those
    }

    return result;
}

int main(int argc, char *argv[]) {
    std::string username = "you";
    std::cout << format("Hello %s! %d", username.c_str(), 123) << std::endl;
    return 0;
}

очень-очень простое решение.

std::string strBuf;
strBuf.resize(256);
int iCharsPrinted = sprintf_s((char *)strPath.c_str(), strPath.size(), ...);
strBuf.resize(iCharsPrinted);

ниже слегка измененная версия ответа @iFreilicht, обновлена до C++14 (использование make_unique функция вместо необработанного объявления) и добавлена поддержка std::string аргументы (на основе Кенни Керра статьи)

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

выход:

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

не стесняйтесь объединять этот ответ с оригинальным, если это необходимо.


Испытанный, Ответ Качества Продукции

этот ответ обрабатывает общий случай с методами, совместимыми со стандартами. Такой же подход приведен в качестве примера на CppReference.com в нижней части страницы.

#include <string>
#include <cstdarg>
#include <vector>

// requires at least C++11
const std::string vformat(const char * const zcFormat, ...) {

    // initialize use of the variable argument array
    va_list vaArgs;
    va_start(vaArgs, zcFormat);

    // reliably acquire the size
    // from a copy of the variable argument array
    // and a functionally reliable call to mock the formatting
    va_list vaArgsCopy;
    va_copy(vaArgsCopy, vaArgs);
    const int iLen = std::vsnprintf(NULL, 0, zcFormat, vaArgsCopy);
    va_end(vaArgsCopy);

    // return a formatted string without risking memory mismanagement
    // and without assuming any compiler or platform specific behavior
    std::vector<char> zc(iLen + 1);
    std::vsnprintf(zc.data(), zc.size(), zcFormat, vaArgs);
    va_end(vaArgs);
    return std::string(zc.data(), iLen); }

#include <ctime>
#include <iostream>
#include <iomanip>

// demonstration of use
int main() {

    std::time_t t = std::time(nullptr);
    std::cerr
        << std::put_time(std::localtime(& t), "%D %T")
        << " [debug]: "
        << vformat("Int 1 is %d, Int 2 is %d, Int 3 is %d", 11, 22, 33)
        << std::endl;
    return 0; }

одно из решений, которое я выбрал, - это сделать это с помощью sprintf непосредственно в буфер std:: string, сделав указанный буфер достаточно большим:

#include <string>
#include <iostream>

using namespace std;

string l_output;
l_output.resize(100);

for (int i = 0; i < 1000; ++i)
{       
    memset (&l_output[0], 0, 100);
    sprintf (&l_output[0], "\r%i", i);

    cout << l_output;
    cout.flush();
}

Итак, создайте строку std::, измените ее размер, получите доступ к ее буферу напрямую...


Poco Foundation библиотека имеет очень удобную функцию форматирования, которая поддерживает std:: string как в строке формата, так и в значениях:


вы можете форматировать вывод C++ в cout с помощью файла заголовка iomanip. Убедитесь, что вы включаете файл заголовка iomanip перед использованием любой из вспомогательных функций, таких как setprecision, setfill etc.

вот фрагмент кода, который я использовал в прошлом для печати среднего времени ожидания в векторе, который я "накопил".

#include<iomanip>
#include<iostream>
#include<vector>
#include<numeric>

...

cout<< "Average waiting times for tasks is " << setprecision(4) << accumulate(all(waitingTimes), 0)/double(waitingTimes.size()) ;
cout << " and " << Q.size() << " tasks remaining" << endl;

вот краткое описание того, как мы можем форматировать потоки C++. http://www.cprogramming.com/tutorial/iomanip.html


могут возникнуть проблемы, если буфер недостаточно велик для печати строки. Перед печатью отформатированного сообщения необходимо определить длину отформатированной строки. Я делаю собственный помощник для этого (протестирован на Windows и Linux GCC), и вы можете попробовать использовать его.

строку.cpp:http://pastebin.com/DnfvzyKP
Строка.ч: http://pastebin.com/7U6iCUMa

строку.cpp:

#include <cstdio>
#include <cstdarg>
#include <cstring>
#include <string>

using ::std::string;

#pragma warning(disable : 4996)

#ifndef va_copy
#ifdef _MSC_VER
#define va_copy(dst, src) dst=src
#elif !(__cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__))
#define va_copy(dst, src) memcpy((void*)dst, (void*)src, sizeof(*src))
#endif
#endif

///
/// \breif Format message
/// \param dst String to store formatted message
/// \param format Format of message
/// \param ap Variable argument list
///
void toString(string &dst, const char *format, va_list ap) throw() {
  int length;
  va_list apStrLen;
  va_copy(apStrLen, ap);
  length = vsnprintf(NULL, 0, format, apStrLen);
  va_end(apStrLen);
  if (length > 0) {
    dst.resize(length);
    vsnprintf((char *)dst.data(), dst.size() + 1, format, ap);
  } else {
    dst = "Format error! format: ";
    dst.append(format);
  }
}

///
/// \breif Format message
/// \param dst String to store formatted message
/// \param format Format of message
/// \param ... Variable argument list
///
void toString(string &dst, const char *format, ...) throw() {
  va_list ap;
  va_start(ap, format);
  toString(dst, format, ap);
  va_end(ap);
}

///
/// \breif Format message
/// \param format Format of message
/// \param ... Variable argument list
///
string toString(const char *format, ...) throw() {
  string dst;
  va_list ap;
  va_start(ap, format);
  toString(dst, format, ap);
  va_end(ap);
  return dst;
}

///
/// \breif Format message
/// \param format Format of message
/// \param ap Variable argument list
///
string toString(const char *format, va_list ap) throw() {
  string dst;
  toString(dst, format, ap);
  return dst;
}


int main() {
  int a = 32;
  const char * str = "This works!";

  string test(toString("\nSome testing: a = %d, %s\n", a, str));
  printf(test.c_str());

  a = 0x7fffffff;
  test = toString("\nMore testing: a = %d, %s\n", a, "This works too..");
  printf(test.c_str());

  a = 0x80000000;
  toString(test, "\nMore testing: a = %d, %s\n", a, "This way is cheaper");
  printf(test.c_str());

  return 0;
}

строку.h:

#pragma once
#include <cstdarg>
#include <string>

using ::std::string;

///
/// \breif Format message
/// \param dst String to store formatted message
/// \param format Format of message
/// \param ap Variable argument list
///
void toString(string &dst, const char *format, va_list ap) throw();
///
/// \breif Format message
/// \param dst String to store formatted message
/// \param format Format of message
/// \param ... Variable argument list
///
void toString(string &dst, const char *format, ...) throw();
///
/// \breif Format message
/// \param format Format of message
/// \param ... Variable argument list
///
string toString(const char *format, ...) throw();

///
/// \breif Format message
/// \param format Format of message
/// \param ap Variable argument list
///
string toString(const char *format, va_list ap) throw();

Я дал ему попробовать, с регулярные выражения. Я реализовал его для строк ints и const в качестве примера, но вы можете добавить любые другие типы (POD типы, но с указателями вы можете напечатать что-нибыдь).

#include <assert.h>
#include <cstdarg>

#include <string>
#include <sstream>
#include <regex>

static std::string
formatArg(std::string argDescr, va_list args) {
    std::stringstream ss;
    if (argDescr == "i") {
        int val = va_arg(args, int);
        ss << val;
        return ss.str();
    }
    if (argDescr == "s") {
        const char *val = va_arg(args, const char*);
        ss << val;
        return ss.str();
    }
    assert(0); //Not implemented
}

std::string format(std::string fmt, ...) {
    std::string result(fmt);
    va_list args;
    va_start(args, fmt);
    std::regex e("\{([^\{\}]+)\}");
    std::smatch m;
    while (std::regex_search(fmt, m, e)) {
        std::string formattedArg = formatArg(m[1].str(), args);
        fmt.replace(m.position(), m.length(), formattedArg);
    }
    va_end(args);
    return fmt;
}

вот пример его использования:

std::string formatted = format("I am {s} and I have {i} cats", "bob", 3);
std::cout << formatted << std::endl;

выход:

Я Боб, и у меня есть 3 кошки


Это можно опробовать. простой. на самом деле не использует нюансы класса string.

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include <string>
#include <exception>
using namespace std;

//---------------------------------------------------------------------

class StringFormatter
{
public:
    static string format(const char *format, ...);
};

string StringFormatter::format(const char *format, ...)
{
    va_list  argptr;

    va_start(argptr, format);

        char   *ptr;
        size_t  size;
        FILE   *fp_mem = open_memstream(&ptr, &size);
        assert(fp_mem);

        vfprintf (fp_mem, format, argptr);
        fclose (fp_mem);

    va_end(argptr);

    string ret = ptr;
    free(ptr);

    return ret;
}

//---------------------------------------------------------------------

int main(void)
{
    string temp = StringFormatter::format("my age is %d", 100);
    printf("%s\n", temp.c_str());

    return 0;
}

_return.desc = (boost::format("fail to detect. cv_result = %d") % st_result).str();