sprintf snprintf entero convertir cadena c++ string stl formatting

c++ - entero - snprintf



std:: cadena de formato como sprintf (30)

Así es como lo hace Google: StringPrintf (Licencia BSD)
y Facebook lo hace de una manera bastante similar: StringPrintf (Licencia Apache)
Ambos proporcionan un conveniente StringAppendF también.

Tengo que formatear std::string con sprintf y enviarlo al flujo de archivos. ¿Cómo puedo hacer esto?


Basado en la respuesta proporcionada por 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(); }

Esto evita la necesidad de desechar la const del resultado de .c_str() que estaba en la respuesta original.


Desafortunadamente, la mayoría de las respuestas aquí utilizan varargs que son intrínsecamente inseguras a menos que se use algo como el atributo de format de GCC que solo funciona con cadenas de formato literal. Puede ver por qué estas funciones no son seguras en el siguiente ejemplo:

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

donde string_format es una implementación de la respuesta de Erik Aronesty. Este código se compila, pero lo más probable es que se bloquee cuando intentes ejecutarlo:

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

Es posible implementar una printf segura y extenderla al formato std::string utilizando plantillas (variadic). Esto se ha hecho en la biblioteca fmt , que proporciona una alternativa segura a sprintf std::string :

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

fmt realiza un seguimiento de los tipos de argumentos y, si el tipo no coincide con la especificación de formato, no existe un error de segmentación, solo una excepción.

Descargo de responsabilidad : Soy el autor de esta biblioteca.


Escribí mi propio uso de vsnprintf para que devuelva la cadena en lugar de tener que crear mi propio búfer.

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

Así que puedes usarlo como

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


Este es el código que utilizo para hacer esto en mi programa ... No es nada sofisticado, pero cumple con el truco ... Tenga en cuenta que tendrá que ajustar su tamaño según corresponda. MAX_BUFFER para mí es 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; }


Mis dos centavos en esta pregunta muy popular.

Para citar la página de manual de funciones similares a printf :

Al regresar con éxito, estas funciones devuelven el número de caracteres impresos (excluyendo el byte nulo usado para finalizar la salida de las cadenas).

Las funciones snprintf () y vsnprintf () no escriben más que bytes de tamaño (incluido el byte nulo de terminación (''/ 0'')). Si la salida se truncó debido a este límite, entonces el valor de retorno es el número de caracteres (excluyendo el byte nulo de terminación) que se habría escrito en la cadena final si hubiera suficiente espacio disponible. Por lo tanto, un valor de retorno de tamaño o más significa que la salida se truncó.

En otras palabras, una implementación sana de C ++ 11 debería ser la siguiente:

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

Funciona bastante bien :)

Las plantillas Variadic solo son compatibles con C ++ 11. La respuesta de pixelpoint muestra una técnica similar utilizando estilos de programación más antiguos.

Es extraño que C ++ no tenga tal cosa fuera de la caja. Recientemente agregaron to_string() , que en mi opinión es un gran paso adelante. Me pregunto si agregarán un operador .format a la std::string eventualmente ...

Editar

Como señaló alexk7, se necesita A +1 en el valor de retorno de std::snprintf , ya que necesitamos espacio para el byte /0 . De manera intuitiva, en la mayoría de las arquitecturas que faltan el +1 , el entero required se sobrescribirá parcialmente con un 0 . Esto sucederá después de la evaluación del parámetro required como real para std::snprintf , por lo que el efecto no debería ser visible.

Sin embargo, este problema podría cambiar, por ejemplo, con la optimización del compilador: ¿qué sucede si el compilador decide utilizar un registro para la variable required ? Este es el tipo de errores que a veces resultan en problemas de seguridad.


No puede hacerlo directamente, porque no tiene acceso de escritura al búfer subyacente (hasta C ++ 11; vea el comment Dietrich Epp). Tendrá que hacerlo primero en una cadena-c, luego copiarlo en una cadena estándar ::

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

Pero no estoy seguro de por qué no usarías un flujo de cadenas. Supongo que tiene razones específicas para no solo hacer esto:

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


Para formatear std::string de una manera ''sprintf'', llame a snprintf (argumentos nullptr y 0 ) para obtener la longitud del búfer necesario. Escriba su función usando la plantilla variadic C ++ 11 como esta:

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

Compile con soporte para C ++ 11, por ejemplo en GCC: g++ -std=c++11

Uso:

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


Podrías probar esto:

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


Si solo desea una sintaxis similar a printf (sin llamar a printf usted mismo), eche un vistazo a Boost Format .


Solución de C ++ 11 que usa vsnprintf() internamente:

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

Un enfoque más seguro y eficiente (lo probé y es más rápido):

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

El valor de fmt_str se pasa para cumplir con los requisitos de va_start .

NOTA: La versión "más segura" y "más rápida" no funciona en algunos sistemas. Por lo tanto, ambos siguen en la lista. Además, "más rápido" depende completamente de que el paso de preasignación sea correcto; de lo contrario, strcpy hace más lento.


Utilizando C++11 sprintf , esto se convierte en una tarea bastante fácil y segura. Veo muchas respuestas a esta pregunta que aparentemente fueron escritas antes del tiempo de C ++ 11 que usan longitudes y vargs de búfer fijos, algo que no recomendaría por razones de seguridad, eficiencia y claridad.

#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 ''/0'' 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 ''/0'' inside }

El fragmento de código anterior está licenciado bajo CC0 1.0 .

Explicación línea por línea:

Objetivo: escribir en un char* utilizando std::snprintf y luego convertirlo en un std::string .

Primero, determinamos la longitud deseada de la matriz de caracteres.

Desde sprintf :

Valor de retorno

[...] Si la cadena resultante se trunca debido al límite buf_size, la función devuelve el número total de caracteres (sin incluir el byte-nulo de terminación) que se habría escrito, si no se impusiera el límite.

Esto significa que el tamaño deseado es el número de caracteres más uno , de modo que el terminador nulo se ubicará después de todos los demás caracteres y que el constructor de cadenas puede recortarlo. Este problema fue explicado por @ alexk7 en los comentarios.

Luego, asignamos una nueva matriz de caracteres y la asignamos a std::unique_ptr . Esto generalmente se recomienda, ya que no tendrá que delete nuevo manualmente.

Tenga en cuenta que esta no es una forma segura de asignar un unique_ptr con tipos definidos por el usuario ya que no puede desasignar la memoria si el constructor lanza una excepción.

Después de eso, podemos, por supuesto, simplemente usar snprintf para su uso previsto y escribir la cadena formateada al char[] y luego crear y devolver una nueva std::string partir de eso.

Puedes ver un ejemplo en acción here .

Si también desea usar std::string en la lista de argumentos, eche un vistazo a esta idea .

Información adicional para usuarios de Visual Studio :

Como se explica en esta respuesta , Microsoft cambió el nombre de std::snprintf a _snprintf (sí, sin std:: _snprintf . MS lo establece como obsoleto y recomienda utilizar _snprintf_s en _snprintf_s lugar, sin embargo, _snprintf_s no aceptará que el búfer sea cero o más pequeño que la salida formateada y no calculará la longitud de las salidas si eso ocurre. Entonces, para deshacerse de las advertencias de desaprobación durante la compilación, puede insertar la siguiente línea en la parte superior del archivo que contiene el uso de _snprintf :

#pragma warning(disable : 4996)


[edit ''17 / 8/31] Agregando una versión variada templada ''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; }

que en realidad es una versión delimitada por comas (en su lugar) de los << operadores que a veces dificultan, utilizados de esta manera:

char chSpace='' ''; double pi=3.1415; std::string sWorld="World", str_var; str_var = vtspf("Hello", '','', chSpace, sWorld, ", pi=", pi);


[editar] Adaptado para hacer uso de la técnica en la respuesta de Erik Aronesty (arriba):

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

[respuesta anterior]
Una respuesta muy tardía, pero para aquellos que, como yo, les gusta la manera ''sprintf'': he escrito y están usando las siguientes funciones. Si te gusta, puedes expandir las% -options para que se ajusten más a las sprintf; los que hay actualmente son suficientes para mis necesidades. Utiliza stringf () y stringfappend () igual que lo haría con sprintf. Solo recuerda que los parámetros para ... deben ser tipos de 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; }


string no tiene lo que necesitas, pero std :: stringstream tiene. Use una cadena de caracteres para crear la cadena y luego extraiga la cadena. Here hay una lista completa de las cosas que puedes hacer. Por ejemplo:

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

le dará 10 decimales de precisión cuando imprima un doble o flotante.



boost::format() proporciona la funcionalidad que desea:

A partir de la sinopsis de las bibliotecas de formatos de Boost:

Un objeto de formato se construye a partir de una cadena de formato, y luego se le dan argumentos mediante llamadas repetidas al operador%. Cada uno de esos argumentos se convierte en cadenas, que a su vez se combinan en una cadena, de acuerdo con la cadena de formato.

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


Probado, respuesta de calidad de producción

Esta respuesta maneja el caso general con técnicas que cumplen con los estándares. El mismo enfoque se ofrece como ejemplo en CppReference.com cerca de la parte inferior de la página.

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


Muy, muy simple solución.

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


Una solución que he preferido es hacer esto con sprintf directamente en el búfer std :: string, después de hacer que dicho búfer sea lo suficientemente grande:

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

Entonces, cree la cadena std ::, cambie su tamaño, acceda a su búfer directamente ...


Usualmente uso esto:

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

Desventaja: no todos los sistemas soportan vasprint


A continuación la versión ligeramente modificada de la respuesta de @iFreilicht, actualizada a C++14 (uso de la make_uniquefunción en lugar de la declaración sin formato) y soporte adicional para std::stringargumentos (basado en el article Kenny Kerr )

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

Salida:

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

Siéntase libre de fusionar esta respuesta con la original si lo desea.


Esto puede ser probado. sencillo. Aunque realmente no usa matices de la clase de cadena.

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


Lo intenté, con expresiones regulares . Lo implementé para las cadenas de caracteres ints y const, como ejemplo, pero puede agregar cualquier otro tipo ( tipos POD pero con punteros puede imprimir cualquier cosa).

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

Aquí hay un ejemplo de uso de él:

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

Salida:

Soy bob y tengo 3 gatos


Puede formatear la salida de C ++ en cout usando el archivo de cabecera iomanip. Asegúrese de incluir el archivo de cabecera iomanip antes de usar cualquiera de las funciones de ayuda, como setprecision, setfill, etc.

Aquí hay un fragmento de código que he usado en el pasado para imprimir el tiempo de espera promedio en el vector, que he "acumulado".

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

Aquí hay una breve descripción de cómo podemos formatear los flujos de C ++. http://www.cprogramming.com/tutorial/iomanip.html


Puede haber problemas, si el búfer no es lo suficientemente grande como para imprimir la cadena. Debe determinar la longitud de la cadena formateada antes de imprimir un mensaje formateado allí. Hago ayuda propia para esto (probado en Windows y Linux GCC ), y puedes intentar usarlo.

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


Si está en un sistema que tiene asprintf(3) , puede envolverlo fácilmente:

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


Tomó la idea de Dacav y la respuesta de pixelpoint . Jugué un poco y conseguí esto:

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

Con sana práctica de programación Creo que el código debe ser suficiente, sin embargo todavía estoy abierto a alternativas más seguras que todavía son bastante simples y no requerirían C ++ 11.

Y aquí hay otra versión que utiliza un búfer inicial para evitar una segunda llamada vsnprintf()cuando el búfer inicial ya es suficiente.

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

(Resulta que esta versión es simplemente similar a la respuesta de Piti Ongmongkolkul , solo que no usa newy delete[], y también especifica un tamaño al crear std::string.

La idea aquí de no usar newy delete[]es implicar el uso de la pila sobre el montón, ya que no necesita llamar a las funciones de asignación y desasignación, sin embargo, si no se usa correctamente, podría ser peligroso bloquear los desbordamientos en algunos (quizás antiguo, o quizás solo sistemas vulnerables). Si esto es una preocupación, sugiero altamente usar newy en su delete[]lugar. Tenga en cuenta que la única preocupación aquí es acerca de las asignaciones como vsnprintf()ya se llama con límites, por lo que la especificación de un límite basado en el tamaño asignado en el segundo búfer también evitaría esas.)


_return.desc = (boost::format("fail to detect. cv_result = %d") % st_result).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); }


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

Usando C99 snprintf y C ++ 11