フォーマットする必要がありますstd::string
とsprintf
それをファイル ストリームに送信します。どうすればこれができるでしょうか?
ベストアンサー1
最新の C++ ではこれが非常に簡単になります。
C++20
C++20紹介するstd::format
は、まさにそれを可能にします。これは、次のような置換フィールドを使用します。Pythonのもの:
#include <iostream>
#include <format>
int main() {
std::cout << std::format("Hello {}!\n", "world");
}
コード元翻訳元、CC BY-SA および GFDL
チェックしてくださいコンパイラサポートページ標準ライブラリ実装で利用できるかどうかを確認します。
2023-07-18 現在、部分的なサポートは以下からご利用いただけます:
- Visual Studio 2019 16.10、2021-05-25にリリース
- クラン 14、機能はここで追跡されます
- GCC 13.1、2023-04-26にリリースけれどサポートは公式には「実験的」です。
それ以外の場合は、以下のC++11ソリューションを使用するか、図書館{fmt}
は と同じ意味を持ちますstd::format
。
C++11
とC++11sstd::snprintf
、これはすでにかなり簡単で安全な作業になりました。
#include <memory>
#include <string>
#include <stdexcept>
template<typename ... Args>
std::string string_format( const std::string& format, Args ... args )
{
int size_s = std::snprintf( nullptr, 0, format.c_str(), args ... ) + 1; // Extra space for '\0'
if( size_s <= 0 ){ throw std::runtime_error( "Error during formatting." ); }
auto size = static_cast<size_t>( size_s );
std::unique_ptr<char[]> buf( new char[ size ] );
std::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。
行ごとの説明:
目的:char*
を使用してに書き込みstd::snprintf
、それを に変換しますstd::string
。
まず、特別な条件を使用してchar配列の必要な長さを決定しますsnprintf
。翻訳元:
戻り値
[...] 結果の文字列が buf_size 制限のために切り捨てられる場合、関数は、制限が課されていなかった場合に書き込まれた文字の合計数 (終了の null バイトは含まない) を返します。
これは、必要なサイズが文字数に 1 を加えた数であることを意味します。これにより、ヌル終端文字が他のすべての文字の後に配置され、文字列コンストラクターによって再び切り捨てられるようになります。この問題は、コメントで @alexk7 によって説明されました。
int size_s = std::snprintf( nullptr, 0, format.c_str(), args ... ) + 1;
snprintf
エラーが発生した場合は負の数を返すので、フォーマットが期待どおりに機能したかどうかを確認します。これを行わないと、コメントで @ead が指摘しているように、サイレント エラーが発生したり、巨大なバッファーが割り当てられたりする可能性があります。
if( size_s <= 0 ){ throw std::runtime_error( "Error during formatting." ); }
size_s
が負になることはないことがわかっているので、静的キャストを使用して signed からint
unsigned に変換しますsize_t
。この方法であれば、最も細かいことにこだわるコンパイラでも、次の行で発生する変換について文句を言うことはありません。
size_t size = static_cast<size_t>( size_s );
次に、新しい文字配列を割り当てて に割り当てますstd::unique_ptr
。手動で再度削除する必要がなくなるため、通常はこれが推奨されます。
unique_ptr
コンストラクターが例外をスローするとメモリの割り当てを解除できないため、これはユーザー定義型で を割り当てる安全な方法ではないことに注意してください。
std::unique_ptr<char[]> buf( new char[ size ] );
でC++14代わりに、make_unique
これはユーザー定義型に対して安全です。
auto buf = std::make_unique<char[]>( size );
その後は、もちろん、snprintf
本来の用途に使用して、フォーマットされた文字列を に書き込むことができますchar[]
。
std::snprintf( buf.get(), size, format.c_str(), args ... );
std::string
最後に、末尾の null 終端文字を省略して、そこから新しいものを作成して返します。
return std::string( buf.get(), buf.get() + size - 1 );
実際の例を見ることができますここ。
std::string
引数リストでも使用したい場合は、この要点。
追加情報ビジュアルスタジオユーザー:
で説明したようにこの答えマイクロソフトはstd::snprintf
に名前を変更しました_snprintf
(はい、 なしstd::
)。MSはさらにこれを非推奨とし、_snprintf_s
代わりに、_snprintf_s
バッファがゼロまたはフォーマットされた出力より小さいことは受け入れられず、その場合は出力の長さを計算しません。そのため、コンパイル時に非推奨の警告を取り除くには、以下を挿入します。次の行ファイルの先頭に次の使用が含まれます_snprintf
:
#pragma warning(disable : 4996)
最終的な考え
この質問に対する回答の多くは C++11 以前に書かれており、固定バッファ長または vargs を使用しています。古いバージョンの C++ にこだわっているのでなければ、これらのソリューションを使用することはお勧めしません。理想的には、C++20 の方法を採用してください。
この回答の C++11 ソリューションはテンプレートを使用するため、頻繁に使用するとかなりの量のコードが生成されることがあります。ただし、バイナリ用のスペースが非常に限られている環境向けに開発しているのでなければ、これは問題にはなりません。また、明瞭性とセキュリティの両面で他のソリューションよりも大幅に改善されています。
スペース効率が非常に重要な場合は、vargsとvsnprintfを使用したこれら2つのソリューション役に立つかもしれません。固定バッファ長のソリューションは使用しないでください。トラブルを招くだけです。