I have to format std::string
with sprintf
and send it into file stream. How can I do this?
41 に答える
基になるバッファへの書き込みアクセス権がないため、直接実行することはできません(C ++ 11まで。DietrichEppのコメントを参照)。最初にc-stringで実行してから、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();
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
レンダリングが遅くなります。
boost::format()
必要な機能を提供します。
Boostフォーマットライブラリの概要からのように:
フォーマット オブジェクトはフォーマット文字列から作成され、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"
C++20 は、API の点ではstd::format
似ていsprintf
ますが、完全にタイプ セーフであり、ユーザー定義型で動作し、Python に似た書式文字列構文を使用します。フォーマットstd::string
してストリームに書き込む方法は次のとおりです。
std::string s = "foo";
std::cout << std::format("Look, a string: {}", s);
または、 {fmt} ライブラリを使用して文字列をフォーマットし、それをstdout
ファイル ストリームに一度に書き込むこともできます。
fmt::print("Look, a string: {}", s);
ここでの他の回答のほとんどについては、残念ながら可変引数を使用しており、リテラル形式の文字列でのみ機能するsprintf
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
免責事項: 私は {fmt} と C++20 の作成者std::format
です。
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);
(自分でprintfを呼び出さずに)printfのような構文だけが必要な場合は、BoostFormatを参照してください。
std::string
「sprintf」方式でフォーマットするには、 snprintf
(arguments nullptr
and 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 str;
}
GCC などで C++11 サポートを使用してコンパイルします。g++ -std=c++11
使用法:
std::cout << string_sprintf("%g, %g\n", 1.23, 0.001);
[編集: 20/05/25] さらに良い...:
ヘッダー内:
// `say` prints the values
// `says` returns a string instead of printing
// `sayss` appends the values to it's first argument instead of printing
// `sayerr` prints the values and returns `false` (useful for return statement fail-report)<br/>
void PRINTSTRING(const std::string &s); //cater for GUI, terminal, whatever..
template<typename...P> void say(P...p) { std::string r{}; std::stringstream ss(""); (ss<<...<<p); r=ss.str(); PRINTSTRING(r); }
template<typename...P> std::string says(P...p) { std::string r{}; std::stringstream ss(""); (ss<<...<<p); r=ss.str(); return r; }
template<typename...P> void sayss(std::string &s, P...p) { std::string r{}; std::stringstream ss(""); (ss<<...<<p); r=ss.str(); s+=r; } //APPENDS! to s!
template<typename...P> bool sayerr(P...p) { std::string r{}; std::stringstream ss("ERROR: "); (ss<<...<<p); r=ss.str(); PRINTSTRING(r); return false; }
PRINTSTRING(r)
-function は、GUI または端末、または を使用した特別な出力ニーズに対応するための#ifdef _some_flag_
ものです。デフォルトは次のとおりです。
void PRINTSTRING(const std::string &s) { std::cout << s << std::flush; }
[編集 '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;
}
<<
これは事実上、次のように使用される、時々妨げとなる -operatorsの (代わりに) コンマ区切りバージョンです。
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」方式が好きな人のために: 私は次の関数を書き、使用しています。気に入った場合は、%-options を拡張して 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;
}
この非常に人気のある質問に私の 2 セント。
-like functionsのマンページprintf
を引用するには:
正常に返されると、これらの関数は出力された文字数を返します (文字列への出力を終了するために使用される null バイトを除く)。
関数 snprintf() および vsnprintf() は size バイト (終端の null バイト ('\0') を含む) を超えて書き込みません。この制限のために出力が切り捨てられた場合、戻り値は、十分なスペースが利用可能であった場合に最終的な文字列に書き込まれた文字数 (終端の null バイトを除く) です。したがって、サイズ以上の戻り値は、出力が切り捨てられたことを意味します。
つまり、適切な 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 が指摘したように、バイト用のスペースが+1
必要なため、 の戻り値に A が必要です。直観的に、ほとんどのアーキテクチャでが欠落していると、整数が で部分的に上書きされます。これはの実パラメータとして を評価した後に発生するため、効果は表示されません。std::snprintf
\0
+1
required
0
required
std::snprintf
ただし、この問題は、たとえばコンパイラの最適化によって変わる可能性があります。コンパイラがrequired
変数にレジスタを使用することを決定した場合はどうなるでしょうか? これは、セキュリティ上の問題を引き起こすことがあるエラーの一種です。
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();
}
これにより、元の回答にあっconst
た結果をキャストする必要がなくなります。.c_str()
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;
}
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桁の精度が得られます。
これを試すことができます:
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 );
私は通常これを使用します:
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 をサポートしているわけではない
更新 1 : 追加されたfmt::format
テスト
私はここで紹介した方法について独自の調査を行い、ここで述べたものと正反対の結果を得ました。
私は4つのメソッドで4つの関数を使用しました:
- 可変長関数 +
vsnprintf
+std::unique_ptr
- 可変長関数 +
vsnprintf
+std::string
- 可変個引数テンプレート関数 +
std::ostringstream
+std::tuple
+utility::for_each
fmt::format
fmt
ライブラリからの関数
テスト バックエンドには、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
実装はここから取られます:タプルを繰り返す
#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);
}
未使用.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;
}
}
結果:
[ 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
ます。
これは、私のプログラムでこれを行うために使用するコードです...派手なことは何もありませんが、うまくいきます...注意してください。必要に応じてサイズを調整する必要があります。私の場合、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;
}
@iFreilichtの回答のわずかに変更されたバージョンの下で、C++ 14make_unique
に更新され(未加工の宣言の代わりに関数を使用)、引数のサポートが追加されました(Kenny Kerrの記事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
必要に応じて、この回答を元の回答と自由にマージしてください。
Poco Foundationライブラリには、フォーマット文字列と値の両方で std::string をサポートする非常に便利なフォーマット関数があります。
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) 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;
}
ノート:
- VC++ は廃止することを決定したため、別の VC++ コード ブランチが必要です
snprintf
。これにより、上記の他の投票数の多い回答に対してコンパイラの警告が生成されます。私はいつも「エラーとして警告」モードで実行しているので、それは私にとってはうまくいきません。 char *
関数はの代わりに受け入れますstd::string
。これは、ほとんどの場合、この関数は実際char *
には ではなく、リテラル文字列で呼び出されるためstd::string
です。フォーマットパラメータとして持っている場合はstd::string
、 を呼び出すだけ.c_str()
です。- 関数の名前は、printf、scanf などに対応するため、string_format などではなく stringf です。
- 安全性の問題には対応していません (つまり、不適切なパラメーターは、例外ではなくセグ フォールトを引き起こす可能性があります)。これが必要な場合は、boost またはfmtライブラリを使用することをお勧めします。ここでの私の好みは fmt です。プロジェクトにドロップするヘッダーとソース ファイルが 1 つだけであり、boost よりも奇妙な書式設定構文が少ないためです。ただし、両方とも printf フォーマット文字列と互換性がないため、その場合でも以下が役立ちます。
- stringf コードは、 GCC 厳密モード コンパイルを通過します。これには
#pragma
、GCC 警告の誤検知を抑制する追加のマクロが必要です。
上記のコードはテスト済みで、
これは試すことができます。単純。ただし、文字列クラスのニュアンスは実際には使用しません。
#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;
}
バッファーが文字列を出力するのに十分な大きさでない場合、問題が発生する可能性があります。そこにフォーマットされたメッセージを出力する前に、フォーマットされた文字列の長さを決定する必要があります。私はこれに対して独自のヘルパーを作成し (Windows および Linux GCCでテスト済み)、試してみることができます。
String.cpp: http://pastebin.com/DnfvzyKP
String.h: 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();
非常にシンプルなソリューションです。
std::string strBuf;
strBuf.resize(256);
int iCharsPrinted = sprintf_s((char *)strPath.c_str(), strPath.size(), ...);
strBuf.resize(iCharsPrinted);
私が好んだ解決策の 1 つは、バッファを十分に大きくした後、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 を作成し、サイズを変更し、そのバッファに直接アクセスします...
正規表現で試してみました。例としてintと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匹の猫を飼っています
Visual 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());
}