web-dev-qa-db-ja.com

spdintfのようなstd ::文字列フォーマット

std::stringsprintf でフォーマットしてファイルストリームに送る必要があります。これどうやってするの?

381
Max Frai

基礎となるバッファへの書き込みアクセス権がないため、直接行うことはできません(C++ 11まで。DietrichEppの comment を参照)。最初に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();
285
Doug T.

内部的にvsnprintf()を使用するC++ 11ソリューション:

#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によって遅くなります。

237
Erik Aronesty

C++ 11std::snprintf を利用すると、これはかなり簡単で安全な作業になります。私は、安全性、効率性、明快さの理由から、私がお勧めしない、固定バッファ長とvargsを使用するC++ 11以前に書かれたこの質問に対する多くの答えを見ます。

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

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

上記のコードスニペットは CC0 1.0 の下でライセンスされています。

行ごとの説明:

Aim:char*を使用してstd::snprintfに書き込み、それをstd::stringに変換します。

まず、目的のchar配列の長さを決めます。

からcppreference.com

戻り値

[...] buf_size制限のために結果の文字列が切り捨てられた場合、制限が課されていなければ、functionは書き込まれたであろう合計文字数(終端のnullバイトを含まない)を返します。

これは、必要なサイズが文字数 と1 であることを意味します。したがって、NULLターミネータは他のすべての文字の後に配置され、文字列コンストラクタによって再び切り捨てられます。この問題はコメントの中で@ alexk7によって説明されていました。

次に、新しい文字配列を割り当て、それをstd::unique_ptrに割り当てます。手動で再度deleteする必要はないので、これは一般的にお勧めです。

コンストラクタが例外を投げればメモリを解放することができないので、これはunique_ptrにユーザ定義型を割り当てる安全な方法ではないことに注意してください。

その後は、もちろんその用途にsnprintfを使用し、フォーマットされた文字列をchar[]に書き込み、それから新しいstd::stringを作成して返すことができます。


あなたは例のアクションを見ることができます ここ


引数リストでstd::stringも使用したい場合は、 this Gist をご覧ください。


Visual Studio usersの追加情報

この答え で説明されているように、Microsoftはstd::snprintf_snprintfに変更しました(はい、std::なしで)。 MSはそれを非推奨としてさらに設定し、代わりに _snprintf_s を使用することをお勧めしますが、_snprintf_sはバッファをフォーマットされた出力よりゼロ以下にすることを受け入れず、出力長を計算しません。そのため、コンパイル中に廃止予定の警告を取り除くために、ファイルの先頭に/ _snprintfの使用を含む 次の行 を挿入できます。

#pragma warning(disable : 4996)
193
iFreilicht

boost::format() は必要な機能を提供します。

Boostフォーマットライブラリの概要と同じです。

フォーマットオブジェクトはformat-stringから構築され、次にoperator%への繰り返し呼び出しを通して引数が与えられます。これらの引数はそれぞれ文字列に変換され、format-stringに従って1つの文字列に結合されます。

#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"
100
kennytm

残念ながら、ここでの回答のほとんどは、リテラルフォーマット文字列でしか機能しないGCCのformat属性のようなものを使用しない限り、本質的に安全ではない可変引数を使用しています。次の例で、これらの関数がなぜ安全ではないのかわかります。

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

string_formatはErik Aronestyの答えからの実装です。このコードはコンパイルされますが、実行しようとするとクラッシュする可能性があります。

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

安全なprintfを実装し、(variadic)テンプレートを使用してstd::stringをフォーマットするように拡張することは可能です。これは {fmt}ライブラリ で行われており、sprintfstd::stringを返す代わりに安全です。

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

{fmt}は引数の型を追跡し、その型が書式指定に一致しない場合はセグメンテーションフォルトはありません。constexpr形式の文字列チェックが使用されている場合は例外またはコンパイル時エラーのみが発生します。

免責事項 :私は{fmt}の作者です。

32
vitaut

Printfのような構文だけが欲しい場合(自分でprintfを呼び出さずに)、 Boost Format を見てください。

18
Timo Geusch

Vsnprintfを使って自分で書いたので、自分のバッファを作らなくてもstringを返します。

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

[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のものに合うように%オプションを拡張することができます。そこにあるものは現在私のニーズには十分です。 sprintfと同じように、stringf()とstringfappend()を使用します。 ...のパラメータは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;
}
14
slashmais

std::stringを 'sprintf'の形式でフォーマットするには、必要なバッファの長さを取得するためにsnprintf(引数nullptr0)を呼び出します。このように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);
}

GCCなどで、C++ 11サポートを使用してコンパイルします。g++ -std=c++11

使用法:

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

これがGoogleのやり方です: StringPrintf (BSDライセンス)
とfacebookはそれを非常によく似た方法で行います: StringPrintf (Apacheライセンス)
どちらも便利なStringAppendFも提供します。

10
PW.

この非常に人気のある質問に私の2セント。

printf-like関数のマンページを引用するには

正常に終了すると、これらの関数は印刷された文字数を返します(文字列への出力を終了するために使用されるNULLバイトを除く)。

関数snprintf()とvsnprintf()は、sizeバイト(終端のヌルバイト( '\ 0')を含む)を超えて書き込まない。この制限のために出力が切り捨てられた場合、戻り値は、十分なスペースがある場合に最終ストリングに書き込まれたはずの文字数(終端のヌルバイトを除く)です。したがって、size以上の戻り値は、出力が切り捨てられたことを意味します。

言い換えれば、正しい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);
}

それはかなりうまくいきます:)

可変テンプレートはC++ 11でのみサポートされています。 pixelpointからの回答は、古いプログラミングスタイルを使用した同様の手法を示しています。

C++が箱から出してそのようなことをしないのは奇妙なことです。彼らは最近 to_string() を追加しました。これは私の意見では大きな前進です。結局、彼らが.formatstd::string演算子を追加するのではないかと思っています...

編集する

Alexk7が指摘したように、+1バイトにはスペースが必要なので、std::snprintfの戻り値には\0が必要です。直感的に、ほとんどのアーキテクチャで+1がないと、required整数は0で部分的に上書きされます。これはafter _ std::snprintfの実パラメータとしてのrequiredの評価が起こるため、効果は見えないはずです。

しかし、この問題は、たとえばコンパイラの最適化によって変化する可能性があります。コンパイラがrequired変数にレジスタを使用することにした場合はどうなりますか。これはセキュリティ上の問題を引き起こすことがある一種のエラーです。

9
Dacav
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を使う

7
emerge

テスト済み、製造品質回答

この回答では、標準に準拠した手法で一般的なケースを扱います。同じアプローチが 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; }

予測可能な線形効率

2つのパスは、質問の仕様に従って、安全で信頼性が高く、予測可能な再利用可能な機能のために必要です。再利用可能な関数内のvargsのサイズの分布についての推定は、プログラミングスタイルが良くないため避けるべきです。この場合、vargsの任意に大きな可変長表現は、アルゴリズムの選択における重要な要素です。

オーバーフロー時の再試行は指数関数的に非効率的です。これは、書き込みバッファがnullの場合にドライランを提供するというC++ 11標準委員会が上記の提案を検討したときに検討されたもう1つの理由です。

上記の生産準備完了実装では、最初の実行は割り当てサイズを決定するためのこのような空の実行です。割り当ては行われません。 printfディレクティブの解析とvargsの読み取りは、何十年にも渡って非常に効率的になりました。たとえ些細な場合にわずかな非効率性を犠牲にしなければならないとしても、再利用可能なコードは予測可能であるべきです。

セキュリティと信頼性

Andrew Koenig氏は、ケンブリッジのイベントでの講演の後、「ユーザ機能は予期しない機能の失敗を悪用することに頼るべきではない」と述べた。いつものように、彼の知恵はそれ以来記録に真実が示されてきた。修正および閉鎖されたセキュリティバグの問題は、修正前に悪用されたホールの説明に再試行ハックがあることを示していることがよくあります。

これは、 sprintfの代替案、C9X改訂提案、ISO IEC文書WG14 N6​​45/X3J11 96-008 のヌルバッファ機能に関する正式な規格改訂提案で述べられています。動的メモリの可用性の制約内でprintディレクティブごとに挿入された任意の長さの文字列 "%s"も例外ではなく、 "予期しない機能"を生成するために悪用されるべきではありません。

この回答の最初の段落にリンクされているC++ Reference.orgのページの下部に示されているコード例と共に提案を検討してください。

また、失敗事例のテストが成功事例と同程度に堅牢であることはめったにありません。

移植性

すべての主要O.Sベンダーは、c ++ 11標準の一部としてstd :: vsnprintfを完全にサポートするコンパイラを提供しています。ディストリビューションを管理しなくなったベンダーの製品を実行しているホストには、さまざまな理由からg ++またはclang ++を提供する必要があります。

スタック使用

Std :: vsnprintfへの最初の呼び出しでのスタック使用量は、2番目の呼び出しのスタック使用量以下になり、2番目の呼び出しが始まる前に解放されます。最初の呼び出しがスタックの可用性を超えると、std :: fprintfも失敗します。

6
Douglas Daseeco

Erik Aronestyが提供した回答に基づいて:

std::string string_format(const std::string &fmt, ...) {
    std::vector<char> str(100,'\0');
    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();
}

これにより、元の答えにあった.c_str()の結果からconstを捨てる必要がなくなります。

5
ChetS
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);
}
5
pixelpoint

stringには必要なものがありませんが、std :: stringstreamにはあります。文字列ストリームを使用して文字列を作成してから文字列を抽出します。 ここ はあなたができることの包括的なリストです。例えば:

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

doubleまたはfloatを印刷するとき、小数点以下10桁の精度を与えます。

3
Hassan Syed

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;
}
3
Thomas Perl

あなたはこれを試すことができます:

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

以下の@iFreilicht回答を少し修正したものを C++ 14 (生の宣言ではなくmake_unique関数の使用)に更新し、std::string引数のサポートを追加しました(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";
}

出力:

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

必要に応じて、この回答と元の回答を自由に組み合わせてください。

2

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

saneを使うと、コードは十分であるはずですが、それでもまだ十分に単純でC++ 11を必要としない、より安全な代替手段を選択できます。


初期バッファがもう十分なときに、vsnprintf()への2回目の呼び出しを防ぐために初期バッファを利用する別のバージョンがあります。

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の答え によく似ていますが、newdelete[]を使用せず、std::stringを作成するときのサイズも指定します。

ここでnewdelete[]を使用しないという考えは、割り当てや割り当て解除の関数を呼び出す必要がないので、ヒープ上のスタックの使用を暗示することです。あるいは、おそらく脆弱なシステムです。これが心配な場合は、代わりにnewdelete[]を使用することを強くお勧めします。 vsnprintf()はすでに制限付きで呼び出されているため、ここでの唯一の懸念は割り当てに関することです。そのため、2番目のバッファに割り当てられたサイズに基づいて制限を指定すると、制限も回避されます。)

2
konsolebox

これは私のプログラムでこれを実行するために使用するコードです...それは空想的なものではありませんが、それはトリックを行います...注意、あなたは適切なようにあなたのサイズを調整しなければならないでしょう。私のMAX_BUFFERは1024です。

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

    // -- Empty the buffer properly to ensure no leaks.
    memset(textString, '\0', 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;
}
2
Dave

私は通常これを使います。

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をサポートしているわけではありません

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

Iomanipヘッダファイルを使用してCoutでC++出力をフォーマットできます。 setprecision、setfillなどのヘルパー関数を使用する前に、必ずiomanipヘッダーファイルをインクルードしてください。

これは、私が「累積」した平均待ち時間をベクトルで表示するために過去に使用したコードスニペットです。

#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

1
vinkris

UPDATE 1 fmt::formatテストを追加

私はここで紹介した方法について私自身の調査を行い、ここで述べたのと正反対の結果を得ました。

私は4つの方法で4つの関数を使いました:

  • 可変関数+ vsnprintf + std::unique_ptr
  • 可変関数+ vsnprintf + std::string
  • 可変テンプレート関数+ std::ostringstream + std::Tuple + utility::for_each
  • fmtライブラリからのfmt::format関数

テストバックエンドにはgoogletestが使用されています。

#include <string>
#include <cstdarg>
#include <cstdlib>
#include <memory>
#include <algorithm>

#include <fmt/format.h>

inline std::string string_format(size_t string_reserve, const std::string fmt_str, ...)
{
    size_t str_len = (std::max)(fmt_str.size(), string_reserve);

    // plain buffer is a bit faster here than std::string::reserve
    std::unique_ptr<char[]> formatted;

    va_list ap;
    va_start(ap, fmt_str);

    while (true) {
        formatted.reset(new char[str_len]);

        const int final_n = vsnprintf(&formatted[0], str_len, fmt_str.c_str(), ap);

        if (final_n < 0 || final_n >= int(str_len))
            str_len += (std::abs)(final_n - int(str_len) + 1);
        else
            break;
    }

    va_end(ap);

    return std::string(formatted.get());
}

inline std::string string_format2(size_t string_reserve, const std::string fmt_str, ...)
{
    size_t str_len = (std::max)(fmt_str.size(), string_reserve);
    std::string str;

    va_list ap;
    va_start(ap, fmt_str);

    while (true) {
        str.resize(str_len);

        const int final_n = vsnprintf(const_cast<char *>(str.data()), str_len, fmt_str.c_str(), ap);

        if (final_n < 0 || final_n >= int(str_len))
            str_len += (std::abs)(final_n - int(str_len) + 1);
        else {
            str.resize(final_n); // do not forget to shrink the size!
            break;
        }
    }

    va_end(ap);

    return str;
}

template <typename... Args>
inline std::string string_format3(size_t string_reserve, Args... args)
{
    std::ostringstream ss;
    if (string_reserve) {
        ss.rdbuf()->str().reserve(string_reserve);
    }
    std::Tuple<Args...> t{ args... };
    utility::for_each(t, [&ss](auto & v)
    {
        ss << v;
    });
    return ss.str();
}

for_eachの実装はここから取られます: Tupleの上で繰り返します

#include <type_traits>
#include <Tuple>

namespace utility {

    template <std::size_t I = 0, typename FuncT, typename... Tp>
    inline typename std::enable_if<I == sizeof...(Tp), void>::type
        for_each(std::Tuple<Tp...> &, const FuncT &)
    {
    }

    template<std::size_t I = 0, typename FuncT, typename... Tp>
    inline typename std::enable_if<I < sizeof...(Tp), void>::type
        for_each(std::Tuple<Tp...> & t, const FuncT & f)
    {
        f(std::get<I>(t));
        for_each<I + 1, FuncT, Tp...>(t, f);
    }

}

テスト:

TEST(ExternalFuncs, test_string_format_on_unique_ptr_0)
{
    for (size_t i = 0; i < 1000000; i++) {
        const std::string v = string_format(0, "%s+%u\n", "test test test", 12345);
        UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
    }
}

TEST(ExternalFuncs, test_string_format_on_unique_ptr_256)
{
    for (size_t i = 0; i < 1000000; i++) {
        const std::string v = string_format(256, "%s+%u\n", "test test test", 12345);
        UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
    }
}

TEST(ExternalFuncs, test_string_format_on_std_string_0)
{
    for (size_t i = 0; i < 1000000; i++) {
        const std::string v = string_format2(0, "%s+%u\n", "test test test", 12345);
        UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
    }
}

TEST(ExternalFuncs, test_string_format_on_std_string_256)
{
    for (size_t i = 0; i < 1000000; i++) {
        const std::string v = string_format2(256, "%s+%u\n", "test test test", 12345);
        UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
    }
}

TEST(ExternalFuncs, test_string_format_on_string_stream_on_variadic_Tuple_0)
{
    for (size_t i = 0; i < 1000000; i++) {
        const std::string v = string_format3(0, "test test test", "+", 12345, "\n");
        UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
    }
}

TEST(ExternalFuncs, test_string_format_on_string_stream_on_variadic_Tuple_256)
{
    for (size_t i = 0; i < 1000000; i++) {
        const std::string v = string_format3(256, "test test test", "+", 12345, "\n");
        UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
    }
}

TEST(ExternalFuncs, test_string_format_on_string_stream_inline_0)
{
    for (size_t i = 0; i < 1000000; i++) {
        std::ostringstream ss;
        ss << "test test test" << "+" << 12345 << "\n";
        const std::string v = ss.str();
        UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
    }
}

TEST(ExternalFuncs, test_string_format_on_string_stream_inline_256)
{
    for (size_t i = 0; i < 1000000; i++) {
        std::ostringstream ss;
        ss.rdbuf()->str().reserve(256);
        ss << "test test test" << "+" << 12345 << "\n";
        const std::string v = ss.str();
        UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
    }
}

TEST(ExternalFuncs, test_fmt_format_positional)
{
    for (size_t i = 0; i < 1000000; i++) {
        const std::string v = fmt::format("{0:s}+{1:d}\n", "test test test", 12345);
        UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
    }
}

TEST(ExternalFuncs, test_fmt_format_named)
{
    for (size_t i = 0; i < 1000000; i++) {
        const std::string v = fmt::format("{first:s}+{second:d}\n", fmt::arg("first", "test test test"), fmt::arg("second", 12345));
        UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(v);
    }
}

UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR

unsued.hpp

#define UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR(var)   ::utility::unused_param(&var)

namespace utility {

    extern const volatile void * volatile g_unused_param_storage_ptr;

    extern void
#ifdef __GNUC__
    __attribute__((optimize("O0")))
#endif
        unused_param(const volatile void * p);

}

unused.cpp

namespace utility {

    const volatile void * volatile g_unused_param_storage_ptr = nullptr;

    void
#ifdef __GNUC__
    __attribute__((optimize("O0")))
#endif
        unused_param(const volatile void * p)
    {
        g_unused_param_storage_ptr = p;
    }

}

_ results _

[ RUN      ] ExternalFuncs.test_string_format_on_unique_ptr_0
[       OK ] ExternalFuncs.test_string_format_on_unique_ptr_0 (556 ms)
[ RUN      ] ExternalFuncs.test_string_format_on_unique_ptr_256
[       OK ] ExternalFuncs.test_string_format_on_unique_ptr_256 (331 ms)
[ RUN      ] ExternalFuncs.test_string_format_on_std_string_0
[       OK ] ExternalFuncs.test_string_format_on_std_string_0 (457 ms)
[ RUN      ] ExternalFuncs.test_string_format_on_std_string_256
[       OK ] ExternalFuncs.test_string_format_on_std_string_256 (279 ms)
[ RUN      ] ExternalFuncs.test_string_format_on_string_stream_on_variadic_Tuple_0
[       OK ] ExternalFuncs.test_string_format_on_string_stream_on_variadic_Tuple_0 (1214 ms)
[ RUN      ] ExternalFuncs.test_string_format_on_string_stream_on_variadic_Tuple_256
[       OK ] ExternalFuncs.test_string_format_on_string_stream_on_variadic_Tuple_256 (1325 ms)
[ RUN      ] ExternalFuncs.test_string_format_on_string_stream_inline_0
[       OK ] ExternalFuncs.test_string_format_on_string_stream_inline_0 (1208 ms)
[ RUN      ] ExternalFuncs.test_string_format_on_string_stream_inline_256
[       OK ] ExternalFuncs.test_string_format_on_string_stream_inline_256 (1302 ms)
[ RUN      ] ExternalFuncs.test_fmt_format_positional
[       OK ] ExternalFuncs.test_fmt_format_positional (288 ms)
[ RUN      ] ExternalFuncs.test_fmt_format_named
[       OK ] ExternalFuncs.test_fmt_format_named (392 ms)

ご覧のとおり、vsnprintf + std::stringによる実装はfmt::formatと同じですが、vsnprintf + std::unique_ptrによるよりも速く、std::ostringstreamよりも高速です。

テストはVisual Studio 2015 Update 3でコンパイルされ、Windows 7 x64 / Intel Core i7-4820K CPU @ 3.70GHz / 16GBで実行されます。

1
Andry

Poco Foundation libraryには非常に便利なフォーマット関数があり、フォーマット文字列と値の両方でstd :: stringをサポートしています。

1
riot_starter

非常に簡単な解決策。

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

いくつかの答えを更新しました、違いは - 関数は%sのためにstd :: stringを正しく受け入れるでしょう

namespace format_helper
{

    template <class Src>
    inline Src cast(Src v)
    {
        return v;
    }

    inline const char *cast(const std::string& v)
    {
        return v.c_str();
    }
};

template <typename... Ts>
inline std::string stringfmt (const std::string &fmt, Ts&&... vs)
{
    using namespace format_helper;
    char b;
    size_t required = std::snprintf(&b, 0, fmt.c_str(), cast(std::forward<Ts>(vs))...);//not counting the terminating null character.
    std::string result;
    //because we use string as container, it adds extra 0 automatically
    result.resize(required , 0);
    //and snprintf will use n-1 bytes supplied
    std::snprintf(const_cast<char*>(result.data()), required + 1, fmt.c_str(), cast(std::forward<Ts>(vs))...);

    return result;
}

ライブ: http://cpp.sh/5ajsv

1
Alex Zaharov

バッファが文字列を出力するのに十分な大きさでない場合、問題があるかもしれません。フォーマットされたメッセージをそこに印刷する前に、フォーマットされた文字列の長さを決定する必要があります。私はこれに対する独自のヘルパーを作り(WindowsとLinuxでテストされた _ gcc _ )、そしてあなたはそれを使ってみることができます。

String.cpp: http://Pastebin.com/DnfvzyKP
String.h: http://Pastebin.com/7U6iCUMa

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

String.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();

私はこれが何度も答えられてきたことを実感します、しかしこれはもっと簡潔です:

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 も参照してください。

0
Patrick Beard

ビジュアルC :の場合

std::wstring stringFormat(const wchar_t* fmt, ...)
{
    if (!fmt) {
        return L"";
    }

    std::vector<wchar_t> buff;
    size_t size = wcslen(fmt) * 2;
    buff.resize(size);
    va_list ap;
    va_start(ap, fmt);
    while (true) {
        int ret = _vsnwprintf_s(buff.data(), size, _TRUNCATE, fmt, ap);
        if (ret != -1)
            break;
        else {
            size *= 2;
            buff.resize(size);
        }
    }
    va_end(ap);
    return std::wstring(buff.data());
}
0
Jichao

これは試すことができます。簡単です。実際には文字列クラスのニュアンスを使用しません。

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

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

ノート:

  1. VC++はsnprintfを廃止することを決定したため、別のVC++コードブランチが必要です。私はいつも「エラーとしての警告」モードで走っているので、それは私にとって無駄です。
  2. 関数はchar *の代わりにstd::stringを受け入れます。これはほとんどの場合、この関数はchar *ではなく、実際にはstd::stringであるリテラル文字列で呼び出されるためです。 formatパラメータとしてstd::stringがある場合は、.c_str()を呼び出します。
  3. 関数の名前はprintfやscanfなどに対応するためのstring_formatのようなものではなくstringfです。
  4. 安全性の問題には対処していません(すなわち、悪いパラメータは例外ではなく潜在的にsegエラーを引き起こす可能性があります)。これが必要ならば、boostか fmt ライブラリを使ったほうがよい。ここでの私の好みはfmtでしょう。なぜならそれはboostよりも奇妙なフォーマットシンタックスを持ちながらプロジェクトにドロップするのはただ一つのヘッダとソースファイルだからです。しかし、どちらもprintfフォーマット文字列とは互換性がないので、その場合は以下のものがまだ役に立ちます。
  5. Stringfコードは GCC厳密モードコンパイル を通過します。これはGCC警告の誤検知を抑制するために追加の#pragmaマクロを必要とします。

上のコードはテストされました、

0
Shital Shah

私が好んだ一つの解決策は、上記のバッファを十分に大きくした後、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\0", i);

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

そのため、std :: stringを作成し、サイズを変更し、直接バッファにアクセスします。

0
Xelous

正規表現 で試してみました。私は例として整数とconst文字列のためにそれを実装しました、しかし、あなたは他のどんなタイプでも追加することができます( _ pod _ typesがポインタであなたは何でも印刷することができます)。

#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匹の猫を飼っています

0
ElefEnt

私は今Visual Studioのバージョンを書くつもりで、うまくいけばいつか誰かがそれを移植可能にするでしょう。 (疑いのある人は_vsnwprintfvsnwprintfとこのようなものに置き換える必要があります。)

プロジェクト構成からdefine _CRT_SECURE_NO_WARNINGSを使用して、非推奨警告を無効にする必要があります。

私は最初のパラメータをnullptrとして_vsnwprintfを使用してバッファサイズを見積もり、wstringバッファを予約し、そして次に文字列を直接bufferにフォーマットすることができるようにしています。

同じメソッド呼び出し(_vsnwprintf_s)の安全なバージョンでは入力としてnullptrを使用できないため、なぜ非推奨の警告を無効にする必要があるのか​​わからない。容疑者はMicrosoft C++チームに報告する必要があります。

これはUnicode版のみです、 'w'を削除してwchar_tcharに置き換えることで、おそらくASCIIでも動作させることができます。

//
//  Formats wstring according to format.
//
wstring wformat(const wchar_t* format, ...)
{
    va_list args;
    va_start(args, format);
    int size = _vsnwprintf(nullptr, 0, format, args);
    size++; // Zero termination
    wstring ws;
    ws.resize(size);
    _vsnwprintf(&ws[0], size, format, args);
    va_end(args);
    return ws;
}
0
TarmoPikaro

これは、Windowsに固有のソリューションで、Visual Studioでコンパイラの警告を黙らせることなく回避するように設計されています。問題となっている警告はva_startでstd :: stringを使用することです。これは誤って警告を生成し、廃止予定の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 );
0
Lose

ここで私の(簡単な解決策):

std::string Format(const char* lpszFormat, ...)
{
    // Warning : "vsnprintf" crashes with an access violation
    // exception if lpszFormat is not a "const char*" (for example, const string&)

    size_t  nSize     = 1024;
    char    *lpBuffer = (char*)malloc(nSize);

    va_list lpParams;

    while (true)
    {
        va_start(lpParams, lpszFormat);

        int nResult = vsnprintf(
            lpBuffer,
            nSize,
            lpszFormat,
            lpParams
        );

        va_end(lpParams);

        if ((nResult >= 0) && (nResult < (int)nSize) )
        {
            // Success

            lpBuffer[nResult] = '\0';
            std::string sResult(lpBuffer);

            free (lpBuffer);

            return sResult;
        }
        else
        {
            // Increase buffer

            nSize =
                  (nResult < 0)
                ? nSize *= 2
                : (nResult + 1)
            ;

            lpBuffer = (char *)realloc(lpBuffer, nSize);
        }
    }
}
0