Tengo a formato std::stringcon sprintfy 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::formattiene la menor cantidad de líneas de código ... es revisado por pares y se integra muy bien con las secuencias de C ++.std::formatse agregó a C ++ 20 BTW: stackoverflow.com/a/57286312/895245 ¡Impresionante!C++20ayer y vi que seC++20copióboost(por enésima vez) agregando elstd::formata laC++20especificació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::stringsi lo desea, y recuerdefreeel 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 lugardoubledonde%sse 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::snprintfy 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.
snprintfdevolverá 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ádeleteque volver a hacerlo manualmente .Tenga en cuenta que esta no es una forma segura de asignar un
unique_ptrtipo 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
snprintfpara su uso previsto y escribir la cadena formateada en elchar[].Finalmente, creamos y devolvemos uno nuevo a
std::stringpartir 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::stringen 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::snprintfa_snprintf(sí, sinstd::). MS lo configuró como obsoleto y recomienda usarlo_snprintf_sen 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::stringa 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::vectorson, 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_strse 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
strcpyhace más lento.fuente
sizeen 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::formatque se asemejasprintfen 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::stringy escribirlo en una secuencia:o
Alternativamente, puede usar la biblioteca {fmt} para formatear una cadena y escribirla en
stdoutuna secuencia de archivos de una vez:En cuanto a la
sprintfmayoría de las otras respuestas aquí, desafortunadamente usan varargs y son inherentemente inseguras a menos que use algo como elformatatributo 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_formates 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 declaredfmtagregó 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
vsnprintfdirectamente en la cadena.std::unique_ptr<char[]> buffer (new char[size]);Para formatear
std::stringde manera 'sprintf', llame asnprintf(argumentosnullptry0) 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++11Uso:
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::movees 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
fmtreferencia 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
printffunciones 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.formatoperador alstd::stringeventualmente ...Editar
Como señaló alexk7,
+1se necesita A en el valor de retorno destd::snprintf, ya que necesitamos tener espacio para el\0byte. Intuitivamente, en la mayoría de las arquitecturas que faltan+1, elrequiredentero se sobrescribirá parcialmente con a0. Esto sucederá después de la evaluación derequiredcomo 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
requiredvariable? Este es el tipo de errores que a veces resultan en problemas de seguridad.fuente
bytesbúfer, probablemente sobre elrequiredentero (que afortunadamente en ese momento ya está evaluado).nullptrcomo 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++2atodavía no lo admite.La API agregará un nuevo
std::formatencabezado:La
fmtbiblioteca 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
constel resultado del.c_str()cual estaba en la respuesta original.fuente
fuente
_vscprintfes. 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
vsnprintfdirectamente 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
newydelete[], y también especifica un tamaño al crearstd::string.La idea aquí de no usar
newydelete[]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 usarnewy 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_uniquefunción en lugar de declaración en bruto) y soporte adicional parastd::stringargumentos (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);datayc_strson 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::formatpruebas 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_ptrvsnprintf+std::stringstd::ostringstream+std::tuple+utility::for_eachfmt::formatfunción de lafmtbibliotecaPara el backend de prueba se
googletestha utilizado.La
for_eachimplementació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::stringes 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 3y ejecutadas enWindows 7 x64 / Intel Core i7-4820K CPU @ 3.70GHz / 16GB.fuente