Tengo a formato std::string
con sprintf
y enviarlo a secuencia de archivo. ¿Cómo puedo hacer esto?
c++
string
formatting
stdstring
c++-standard-library
Max Frai
fuente
fuente
boost::format
(como la solución de kennytm usa aquí ).boost::format
¡Ya es compatible con los operadores de transmisión C ++ también! ejemplo:cout << format("helloworld. a=%s, b=%s, c=%s") % 123 % 123.123 % "this is a test" << endl;
.boost::format
tiene la menor cantidad de líneas de código ... es revisado por pares y se integra muy bien con las secuencias de C ++.std::format
se agregó a C ++ 20 BTW: stackoverflow.com/a/57286312/895245 ¡Impresionante!C++20
ayer y vi que seC++20
copióboost
(por enésima vez) agregando elstd::format
a laC++20
especificación! Estaba muy muy feliz! Se han utilizado casi todos los archivos C ++ que he escrito en los últimos 9 añosboost::format
. agregar una salida oficial de estilo printf a las transmisiones en C ++ será de gran ayuda IMO para todo C ++.Respuestas:
No puede hacerlo directamente, porque no tiene acceso de escritura al búfer subyacente (hasta C ++ 11; vea el comentario de Dietrich Epp ). Tendrás que hacerlo primero en una cadena en C, luego copiarlo en una cadena estándar:
Pero no estoy seguro de por qué no usarías una secuencia de cadena. Supongo que tiene razones específicas para no solo hacer esto:
fuente
char buf[100];
hace que esta solución no sea muy robusta. Pero la idea esencial está ahí.asprintf
, que asigna una nueva cadena con suficiente espacio para contener el resultado. Luego cópielo a unstd::string
si lo desea, y recuerdefree
el original. Además, es posible poner esto en una macro para que cualquier buen compilador ayude a validar el formato para usted; no desea poner un lugardouble
donde%s
se espera que seaC ++ moderno hace que esto sea súper simple.
C ++ 20
C ++ 20 introduce
std::format
, lo que le permite hacer exactamente eso. Utiliza campos de reemplazo similares a los de Python :¡Mira la documentación completa ! Es una gran mejora en la calidad de vida.
C ++ 11
Con C ++ 11 s
std::snprintf
, esto ya se convirtió en una tarea bastante fácil y segura.El fragmento de código anterior está licenciado bajo CC0 1.0 .
Explicación línea por línea:
Objetivo: escribir en a
char*
usandostd::snprintf
y luego convertir eso en astd::string
.Primero, determinamos la longitud deseada de la matriz de caracteres usando una condición especial en
snprintf
. Desde cppreference.com :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 cortarlo nuevamente. Este problema fue explicado por @ alexk7 en los comentarios.
snprintf
devolverá un número negativo si se produjo un error, por lo que luego verificamos si el formato funcionó como se desea. No hacer esto podría conducir a errores silenciosos o la asignación de un gran búfer, como lo señala @ead en los comentarios.A continuación, asignamos una nueva matriz de caracteres y la asignamos a a
std::unique_ptr
. Esto generalmente se recomienda, ya que no tendrádelete
que volver a hacerlo manualmente .Tenga en cuenta que esta no es una forma segura de asignar un
unique_ptr
tipo definido por el usuario, ya que no puede desasignar la memoria si el constructor arroja una excepción.Después de eso, por supuesto, podemos usarlo
snprintf
para su uso previsto y escribir la cadena formateada en elchar[]
.Finalmente, creamos y devolvemos uno nuevo a
std::string
partir de eso, asegurándonos de omitir el terminador nulo al final.Puedes ver un ejemplo en acción aquí .
Si también desea usar
std::string
en la lista de argumentos, eche un vistazo a esta esencia .Información adicional para usuarios de Visual Studio :
Como se explica en esta respuesta , Microsoft renombró
std::snprintf
a_snprintf
(sí, sinstd::
). MS lo configuró como obsoleto y recomienda usarlo_snprintf_s
en su lugar, sin embargo_snprintf_s
, no aceptará que el búfer sea cero o menor 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
:Pensamientos finales
Muchas respuestas a esta pregunta se escribieron antes de la época de C ++ 11 y usan longitudes de búfer fijas o vargs. A menos que esté atascado con versiones antiguas de C ++, no recomendaría usar esas soluciones. Idealmente, siga el camino C ++ 20.
Debido a que la solución C ++ 11 en esta respuesta usa plantillas, puede generar bastante código si se usa mucho. Sin embargo, a menos que esté desarrollando para un entorno con un espacio muy limitado para binarios, esto no será un problema y sigue siendo una gran mejora con respecto a las otras soluciones, tanto en claridad como en seguridad.
Si la eficiencia del espacio es muy importante, estas dos soluciones con vargs y vsnprintf pueden ser útiles. NO UTILICE ninguna solución con longitudes de búfer fijas, eso es solo pedir problemas.
fuente
return string(&buf[0], size);
decir algo similar. En segundo lugar, si tuviera que devolver una cadena en C como esa, causaría un comportamiento indefinido porque el vector que contiene los valores a los que apunta se invalidará al regresar. En tercer lugar, cuando comencé a aprender C ++, el estándar no definía en qué orden debían almacenarse los elementos dentro de unstd::vector
, por lo que acceder a su almacenamiento a través de un puntero era un comportamiento indefinido. Ahora funcionaría, pero no veo ningún beneficio en hacerlo de esa manera.std::string
a partir del vector convertido implícitamente ( inicialización de copia ), que luego se devuelve como una copia, como sugiere la firma de la función. Además, los elementos de astd::vector
son, y siempre fueron destinados a ser, almacenados contiguamente . Pero entiendo que tal vez no sea beneficioso hacerlo.return string(buf.get(), buf.get() + size);
debería ser, de loreturn string(buf.get(), buf.get() + size - 1);
contrario, obtendrás una cadena con un carácter nulo al final. Encontré que este es el caso en gcc 4.9.Solución C ++ 11 que utiliza
vsnprintf()
internamente:Un enfoque más seguro y más eficiente (lo probé y es más rápido):
El
fmt_str
se pasa por valor para cumplir con los requisitos deva_start
.NOTA: La versión "más segura" y "más rápida" no funciona en algunos sistemas. Por lo tanto, ambos todavía están en la lista. Además, "más rápido" depende por completo de que el paso de preasignación sea correcto, de lo contrario, lo
strcpy
hace más lento.fuente
size
en cada iteración, cuando puede obtenerlo por la primera llamada devsnprintf()
.boost::format()
proporciona la funcionalidad que desea:A partir de la sinopsis de las bibliotecas de formato Boost:
fuente
C ++ 20 incluirá lo
std::format
que se asemejasprintf
en términos de API pero es totalmente seguro para los tipos, funciona con tipos definidos por el usuario y usa una sintaxis de cadena de formato similar a Python. Así es como podrá formatearlostd::string
y escribirlo en una secuencia:o
Alternativamente, puede usar la biblioteca {fmt} para formatear una cadena y escribirla en
stdout
una secuencia de archivos de una vez:En cuanto a la
sprintf
mayoría de las otras respuestas aquí, desafortunadamente usan varargs y son inherentemente inseguras a menos que use algo como elformat
atributo de GCC que solo funciona con cadenas de formato literal. Puede ver por qué estas funciones no son seguras en el siguiente ejemplo: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 intente ejecutarlo:Descargo de responsabilidad : soy el autor de {fmt} y C ++ 20
std::format
.fuente
error: 'fmt' has not been declared
fmt
agregó una implementación similar a C ++ 20! stackoverflow.com/a/57286312/895245 fmt actualmente reclama soporte para ello. ¡Impresionante trabajo!Si solo desea una sintaxis similar a printf (sin llamar a printf usted mismo), eche un vistazo a Boost Format .
fuente
Escribí el mío usando vsnprintf para que devuelva una cadena en lugar de tener que crear mi propio búfer.
Entonces puedes usarlo como
fuente
vsnprintf
directamente en la cadena.std::unique_ptr<char[]> buffer (new char[size]);
Para formatear
std::string
de manera 'sprintf', llame asnprintf
(argumentosnullptr
y0
) para obtener la longitud de búfer necesaria. Escriba su función usando una plantilla variada de C ++ 11 como esta:Compile con soporte C ++ 11, por ejemplo en GCC:
g++ -std=c++11
Uso:
fuente
char buf[length + 1];
lugar dechar* buf = new char[length + 1];
?char[]
ychar*
con nuevo, es que en el primer caso se asignaría buf en la pila. Está bien para búferes pequeños, pero como no podemos garantizar el tamaño de la cadena resultante, es un poco mejor usarlonew
. Por ejemplo, en mi máquinastring_sprintf("value: %020000000d",5)
, imprimo un número escandaloso de ceros a la izquierda antes del número 5, los volcados de núcleo cuando se usa la matriz en la pila, pero funciona bien cuando se usa la matriz asignada dinámicamentenew char[length + 1]
std::move
es superfluo .[editar: 20/05/25] mejor aún ...:
En el encabezado:
La
PRINTSTRING(r)
función es atender la GUI o terminal o cualquier salida especial que se necesite#ifdef _some_flag_
, el valor predeterminado es:[edit '17 / 8/31] Añadiendo una versión variada con plantilla 'vtspf (..)':
que es efectivamente una versión delimitada por comas (en su lugar) de los
<<
operadores a veces obstaculizadores , utilizados de esta manera:[editar] Adaptado para hacer uso de la técnica en la respuesta de Erik Aronesty (arriba):
[respuesta anterior]
Una respuesta muy tardía, pero para aquellos que, como yo, les gusta la forma 'sprintf': he escrito y estoy 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 usaría sprintf. Solo recuerda que los parámetros para ... deben ser tipos POD.
fuente
fmt
referencia funciona bien. (Pero supongo que la gente querrá jugar con juguetes, así que ...) Si hay algún otro supuesto "error", ¿podría explicarlo?Así es como lo hace google:
StringPrintf
(Licencia BSD)y Facebook lo hace de una manera bastante similar:
StringPrintf
(Licencia Apache)Ambas también ofrecen una opción conveniente
StringAppendF
.fuente
Mis dos centavos en esta pregunta muy popular.
Para citar la página de manual de
printf
funciones similares :En otras palabras, una implementación sensata de C ++ 11 debería ser la siguiente:
Funciona bastante bien :)
Las plantillas variables solo se admiten en C ++ 11. La respuesta de pixelpoint muestra una técnica similar usando estilos de programación más antiguos.
Es extraño que C ++ no tenga tal cosa fuera de la caja. Recientemente agregaron
to_string()
, lo que en mi opinión es un gran paso adelante. Me pregunto si agregarán un.format
operador alstd::string
eventualmente ...Editar
Como señaló alexk7,
+1
se necesita A en el valor de retorno destd::snprintf
, ya que necesitamos tener espacio para el\0
byte. Intuitivamente, en la mayoría de las arquitecturas que faltan+1
, elrequired
entero se sobrescribirá parcialmente con a0
. Esto sucederá después de la evaluación derequired
como parámetro real parastd::snprintf
, por lo que el efecto no debe ser visible.Sin embargo, este problema podría cambiar, por ejemplo, con la optimización del compilador: ¿qué sucede si el compilador decide usar un registro para la
required
variable? Este es el tipo de errores que a veces resultan en problemas de seguridad.fuente
bytes
búfer, probablemente sobre elrequired
entero (que afortunadamente en ese momento ya está evaluado).nullptr
como argumento de búfer, eliminando lachar b;
línea en su código. ( Fuente )Uso de C99 snprintf y C ++ 11
fuente
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 da como ejemplo en CppReference.com cerca de la parte inferior de su página. A diferencia de su ejemplo, este código se ajusta a los requisitos de la pregunta y se prueba en campo en robótica y aplicaciones satelitales. También ha mejorado los comentarios. La calidad del diseño se analiza más adelante.
Eficiencia lineal predecible
Dos pases son necesarios para una función reutilizable segura, confiable y predecible según las especificaciones de la pregunta. Las presunciones sobre la distribución de tamaños de vargs en una función reutilizable es un mal estilo de programación y deben evitarse. En este caso, las representaciones arbitrariamente grandes de longitud variable de vargs son un factor clave en la elección del algoritmo.
Volver a intentar el desbordamiento es exponencialmente ineficiente, que es otra razón discutida cuando el comité de estándares C ++ 11 discutió la propuesta anterior para proporcionar una ejecución en seco cuando el búfer de escritura es nulo.
En la implementación lista para producción anterior, la primera ejecución es tan seca para determinar el tamaño de asignación. No se produce asignación. El análisis de las directivas printf y la lectura de vargs se ha hecho extremadamente eficiente durante décadas. El código reutilizable debe ser predecible, incluso si se debe sacrificar una pequeña ineficiencia para casos triviales.
Seguridad y confiabilidad
Andrew Koenig dijo a un pequeño grupo de nosotros después de su conferencia en un evento de Cambridge: "Las funciones del usuario no deberían depender de la explotación de una falla para una funcionalidad excepcional". Como de costumbre, su sabiduría se ha demostrado verdadera en el registro desde entonces. Los problemas de errores de seguridad fijos y cerrados a menudo indican que se han vuelto a intentar hacks en la descripción del agujero explotado antes de la corrección.
Esto se menciona en la propuesta formal de revisión de estándares para la función de búfer nulo en Alternativa a sprintf, Propuesta de revisión C9X , Documento ISO IEC WG14 N645 / X3J11 96-008 . Una cadena arbitrariamente larga insertada por directiva de impresión, "% s", dentro de las restricciones de la disponibilidad de memoria dinámica, no es una excepción, y no debe explotarse para producir, "Funcionalidad no excepcional".
Considere la propuesta junto con el código de ejemplo que figura en la parte inferior de la página C ++ Reference.org vinculada al primer párrafo de esta respuesta.
Además, la prueba de casos de falla rara vez es tan sólida como la de los casos de éxito.
Portabilidad
Todos los principales proveedores de sistemas operativos proporcionan compiladores que son totalmente compatibles con std :: vsnprintf como parte de los estándares de c ++ 11. Los hosts que ejecutan productos de proveedores que ya no mantienen distribuciones deben estar provistos de g ++ o clang ++ por muchas razones.
Uso de pila
El uso de la pila en la primera llamada a std :: vsnprintf será menor o igual que el de la segunda, y se liberará antes de que comience la segunda llamada. Si la primera llamada excede la disponibilidad de la pila, std :: fprintf también fallará.
fuente
C ++ 20
std::format
¡Ha llegado! La característica se describe en: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0645r9.html y utiliza una
.format()
sintaxis similar a Python .Espero que el uso sea como:
Lo intentaré cuando llegue el soporte a GCC, GCC 9.1.0 con
g++-9 -std=c++2a
todavía no lo admite.La API agregará un nuevo
std::format
encabezado:La
fmt
biblioteca existente afirma implementarlo si necesita el polyfill: https://github.com/fmtlib/fmty se mencionó anteriormente en: std :: formato de cadena como sprintf
fuente
Según la respuesta proporcionada por Erik Aronesty:
Esto evita la necesidad de rechazar
const
el resultado del.c_str()
cual estaba en la respuesta original.fuente
fuente
_vscprintf
es. Creo que deberías dar más detalles sobre esta respuesta.string no tiene lo que necesita, pero std :: stringstream sí. Use un flujo de cadena para crear la cadena y luego extraiga la cadena. Aquí hay una lista completa de las cosas que puede hacer. Por ejemplo:
le dará 10 decimales de precisión al imprimir un doble o flotante.
fuente
Podrías probar esto:
fuente
Si está en un sistema que tiene asprintf (3) , puede envolverlo fácilmente:
fuente
format
, ya que le dice a gcc que verifique los tipos de argumentos y dé una advertencia decente con -Wall:std::string format(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
va_end
. "si no se llama a va_end antes de que regrese una función que llama a va_start o va_copy, el comportamiento no está definido". - docsthrow std::bad_alloc();
, ya que no estoy usando excepciones de C ++ en mi base de código, y para las personas que lo hacen, pueden agregarlo fácilmente basado en el comentario fuente y tu comentario aquí.Este es el código que utilizo para hacer esto en mi programa ... No es nada sofisticado, pero hace el truco ... Tenga en cuenta que tendrá que ajustar su tamaño según corresponda. MAX_BUFFER para mí es 1024.
fuente
vsnprintf
directamente en la cadena.Tomó la idea de Dacav y la respuesta de pixelpoint . Jugué un poco y obtuve esto:
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.(Resulta que esta versión es similar a la respuesta de Piti Ongmongkolkul , solo que no usa
new
ydelete[]
, y también especifica un tamaño al crearstd::string
.La idea aquí de no usar
new
ydelete[]
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 desbordar el búfer en algunos (quizás viejos o quizás solo vulnerables) sistemas. Si esto es una preocupación, sugiero usarnew
y en sudelete[]
lugar. Tenga en cuenta que la única preocupación aquí es acerca de las asignaciones comovsnprintf()
ya se llama con límites, por lo que especificar un límite basado en el tamaño asignado en el segundo búfer también evitaría esos).fuente
Usualmente uso esto:
Desventaja: no todos los sistemas admiten vasprint
fuente
A continuación, la versión ligeramente modificada de la respuesta @iFreilicht, actualizada a C ++ 14 (uso de la
make_unique
función en lugar de declaración en bruto) y soporte adicional parastd::string
argumentos (basado en el artículo de Kenny Kerr )Salida:
Si lo desea, puede combinar esta respuesta con la respuesta original.
fuente
La biblioteca de Poco Foundation tiene una función de formato muy conveniente, que admite std :: string tanto en la cadena de formato como en los valores:
fuente
Puede formatear la salida de C ++ en cout usando el archivo de encabezado iomanip. Asegúrese de incluir el archivo de encabezado iomanip antes de utilizar cualquiera de las funciones auxiliares 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".
Aquí hay una breve descripción de cómo podemos formatear flujos C ++. http://www.cprogramming.com/tutorial/iomanip.html
fuente
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 mi propio ayudante 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:
String.h:
fuente
vsnprintf((char *)dst.data(), dst.size() + 1, format, ap);
: ¿es seguro asumir que el búfer de la cadena tiene espacio para un carácter nulo de terminación? ¿Hay implementaciones que no asignan tamaño + 1 caracteres. ¿Sería más seguro hacerlodst.resize(length+1); vsnprintf((char *)dst.data(), dst.size(), format, ap); dst.resize(length);
data
yc_str
son sinónimos.fuente
Solución muy, muy simple.
fuente
Me doy cuenta de que esto ha sido respondido muchas veces, pero esto es más conciso:
ejemplo:
Ver también http://rextester.com/NJB14150
fuente
ACTUALIZACIÓN 1 :
fmt::format
pruebas agregadasTomé mi propia investigación sobre los métodos que introduje aquí y obtuve resultados diametralmente opuestos en comparación con los mencionados aquí.
He usado 4 funciones en 4 métodos:
vsnprintf
+std::unique_ptr
vsnprintf
+std::string
std::ostringstream
+std::tuple
+utility::for_each
fmt::format
función de lafmt
bibliotecaPara el backend de prueba se
googletest
ha utilizado.La
for_each
implementación se toma desde aquí: iterar sobre tuplaLos exámenes:
El
UTILITY_SUPPRESS_OPTIMIZATION_ON_VAR
.unsued.hpp :
unused.cpp :
RESULTADOS :
Como puede ver, la implementación a través de
vsnprintf
+std::string
es igual afmt::format
, pero más rápida que a través devsnprintf
+std::unique_ptr
, que es más rápida que a través destd::ostringstream
.Las pruebas compiladas
Visual Studio 2015 Update 3
y ejecutadas enWindows 7 x64 / Intel Core i7-4820K CPU @ 3.70GHz / 16GB
.fuente