sintaxis formato espacios dev c++ formatting printf ostream

formato - printf y scanf en dev c++



¿Cómo usar C++ std:: ostream con formato similar a printf? (7)

Estoy aprendiendo C ++. cout es una instancia de la clase std::ostream . ¿Cómo puedo imprimir una cadena formateada con ella?

Todavía puedo usar printf , pero quiero aprender más sobre el método de estilo C ++ que puede tomar todos los beneficios de C ++. Creo que esto debería ser posible con std::ostream pero no puedo encontrar la manera correcta.


Ancho del campo

Establecer el ancho del campo es muy simple. Para cada variable, simplemente preceda con "setw (n)". Me gusta esto:

#include <iostream> #include <iomanip> using namespace std; int main() { const int max = 12; const int width = 6; for(int row = 1;row <= max;row++) { for(int col = 1;col <= max;col++) { cout << setw(width) << row * col; } cout << endl; } return 0; }

Observe cómo "setw (n)" controla el ancho del campo, por lo que cada número se imprime dentro de un campo que se mantiene igual ancho independientemente del ancho del número en sí.

- De "Programming / C ++ tutorial" por P. Lutus.


Cuando necesito los dos tipos de seguridad de cout y el formateo rápido y fácil de variables simples de printf (), mezclo los dos de esta manera. Esta es una solución fea, pero hace las cosas por mí cuando necesito mostrar cosas como "02/07/2014 10:05 am" junto con algunas entidades más complejas:

#include <stdio> #include <stdarg> #include <stdlib> #include <iostream> #pragma hdrstop using namespace std; char* print(char* fmt, ...) { static char buffer[80] = ""; va_list argptr; va_start(argptr,fmt); vsprintf(buffer, fmt, argptr); va_end(argptr); return buffer; } #pragma argsused int main(int argc, char* argv[]) { cout << print("/n%06d/n%6d/n%6d/n%010.3f",1,12,123,123.456); system("PAUSE>NUL"); return 0; }


Escribí de forma independiente, pero se me ocurrió una respuesta similar a user3283405

Mi solución usa vasprintf () para lograr el formateo, y usa la sobrecarga del operador de << of std :: ostream para liberar la memoria en el lugar correcto.

Uso:

std::cout << putf(const char *format, ...); //Same format as C printf(3)

Código:

#define _GNU_SOURCE #include <cstdarg> #include <iostream> #include <cstdio> struct putf_r{ char *s; }; putf_r putf(const char *fmt, ...){ va_list ap; va_start(ap, fmt); putf_r a; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-nonliteral" vasprintf(&a.s, fmt, ap); #pragma GCC diagnostic pop va_end(ap); return a; } std::ostream& operator<<(std::ostream& os, putf_r a){ os<<a.s; free(a.s); return os; } int main(){ std::cout << putf("%3d/n", 23) << putf("%a/n", 256.); }

Tenga en cuenta que el compilador no comprueba el formato dentro de putf (), por lo tanto, el indicador del compilador -Wformat-nonliteral no advertirá si hay códigos sospechosos en putf () y usted debe cuidar el problema de las cadenas de formato no controlado .
Información detallada se puede encontrar en GitHub


Lo único que puedes hacer con std::ostream directamente es la conocida -sintaxis << :

int i = 0; std::cout << "this is a number: " << i;

Y hay varios manipuladores de E / S que se pueden usar para influir en el formato, número de dígitos, etc. de enteros, números de punto flotante, etc.

Sin embargo, eso no es lo mismo que las cadenas con formato de printf . C ++ 11 no incluye ninguna instalación que le permita usar el formato de cadena de la misma manera que se usa con printf (excepto printf mismo, que por supuesto puede usar en C ++ si lo desea).

En términos de bibliotecas que brindan funcionalidad de estilo printf , hay boost::format , que habilita código como este (copiado de la sinopsis):

std::cout << boost::format("writing %1%, x=%2% : %3%-th try") % "toto" % 40.23 % 50;

También tenga en cuenta que hay una propuesta para la inclusión del formato de estilo printf en una versión futura de la Norma. Si esto se acepta, puede estar disponible una sintaxis como la siguiente:

std::cout << std::putf("this is a number: %d/n",i);


Para implementar printf uno podría usar los parámetros de la plantilla c ++ 11:

#include <iostream> #include <string> inline std::ostream & mprintf(std::ostream & ostr, const char * fstr) throw() { return ostr << fstr; } template<typename T, typename... Args> std::ostream & mprintf(std::ostream & ostr, const char * fstr, const T & x) throw() { size_t i=0; char c = fstr[0]; while (c != ''%'') { if(c == 0) return ostr; // string is finished ostr << c; c = fstr[++i]; }; c = fstr[++i]; ostr << x; if(c==0) return ostr; // // print the rest of the stirng ostr << &fstr[++i]; return ostr; } template<typename T, typename... Args> std::ostream & mprintf(std::ostream & ostr, const char * fstr, const T & x, Args... args) throw() { size_t i=0; char c = fstr[0]; while (c != ''%'') { if(c == 0) return ostr; // string is finished ostr << c; c = fstr[++i]; }; c = fstr[++i]; ostr << x; if(c==0) return ostr; // string is finished return mprintf(ostr, &fstr[++i], args...); } int main() { int c = 50*6; double a = 34./67.; std::string q = "Hello!"; // put only two arguments // the symbol after % does not matter at all mprintf(std::cout, "%f + %f = %a /n", c, a); // print string object: for real printf one should write q.c_str() mprintf(std::cout, "message: /"%s/". /n", q); // the last argument will be ignored mprintf(std::cout, "%z + %f/n", (long)a, 12, 544 ); }

Salida

300 + 2 = %a message: "Hello!". 2 + 12

Este es un código muy simple y se puede mejorar.

1) La ventaja es que utiliza << para imprimir objetos en el flujo, por lo que puede poner argumentos arbitrarios que pueden emitirse a través de <<.

2) Ignora el tipo de argumento en la cadena con formato: después de% puede representar un símbolo arbitrario, incluso un espacio. El flujo de salida decide cómo imprimir el objeto correspondiente. También es compatible con printf.

3) Una desventaja es que no puede imprimir el símbolo de porcentaje ''%'', uno necesita mejorar ligeramente el código.

4) No puede imprimir números formateados, como% 4.5f

5) Si el número de argumentos es menor que el pronosticado por una cadena formateada, la función simplemente imprime el resto de la cadena.

6) Si el número de argumentos es mayor que el pronosticado por una cadena con formato, los argumentos restantes se ignoran

Uno puede mejorar el código para hacer 2) -6) para imitar completamente el comportamiento de printf. Sin embargo, si sigue las reglas de printf, entonces solo 3) y 4) esencialmente necesitan ser arregladas.


Salida de muestra:

2017-12-20T16:24:47,604144+01:00 Hello, World!

Código (con el uso de put_printf demostrado en put_timestamp):

#include <assert.h> #include <chrono> #include <iomanip> #include <iostream> class put_printf { static constexpr size_t failed = std::numeric_limits<size_t>::max(); // for any explicit error handling size_t stream_size; // excluding ''/0''; on error set to 0 or to "failed" char buf_stack[2048+1]; // MAY be any size that fits on the stack (even 0), SHOULD be (just) large enough for most uses (including ''/0'') std::unique_ptr<char[]> buf_heap; // only used if the output doesn''t fit in buf_stack public: explicit put_printf(const char *format, ...) #if __GNUC__ __attribute__ ((format (printf, 2, 3))) // most compelling reason for not using a variadic template; parameter 1 is implied "this" #endif { va_list args; va_start(args, format); const int res = vsnprintf(buf_stack, sizeof(buf_stack), format, args); va_end(args); if (res < 0) { // easily provoked, e.g., with "%02147483646i/n", i.e., more than INT_MAX-1 significant characters (only observed, no guarantee seen) stream_size = failed; } else if (res < sizeof(buf_stack)) { // preferred path stream_size = res; } else { // not artificially constrained try { const size_t buf_size = static_cast<size_t>(res) + 1; // avoids relying on "res < INT_MAX" (only observed, no guarantee seen) buf_heap.reset(new char[buf_size]); // observed to work even beyond INT_MAX=2^32-1 bytes va_start(args, format); if (vsnprintf(buf_heap.get(), buf_size, format, args) == res) stream_size = res; else stream_size = failed; // can''t happen va_end(args); } catch (const std::bad_alloc&) { // insufficient free heap space (or an environment-specific constraint?) stream_size = failed; } } } friend std::ostream& operator<<(std::ostream& os, const put_printf& self) { if (self.stream_size == failed) { // (placeholder for any explicit error handling) return os; } else { // using write() rather than operator<<() to avoid a separate scan for ''/0'' or unintentional truncation at any internal ''/0'' character return os.write((self.buf_heap ? self.buf_heap.get() : self.buf_stack), self.stream_size); } } }; class put_timestamp { const bool basic = false; const bool local = true; public: friend std::ostream& operator<<(std::ostream& os, const put_timestamp& self) { const auto now = std::chrono::system_clock::now(); const std::time_t now_time_t = std::chrono::system_clock::to_time_t(now); struct tm tm; if ((self.local ? localtime_r(&now_time_t, &tm) : gmtime_r(&now_time_t, &tm)) == nullptr) return os; // TODO: explicit error handling? static_assert(4 <= sizeof(int), ""); const int microseconds = std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch() % std::chrono::seconds(1)).count(); assert(0 <= microseconds && microseconds < 1000000); // TODO: (how) do we know? // TODO: doesn''t "point" in "decimal_point()" imply "dot"/"full stop"/"period", unlike an obviously neutral term like "mark"/"separator"/"sign"? const char decimal_sign = std::use_facet<std::numpunct<char>>(os.getloc()).decimal_point() == ''.'' ? ''.'' : '',''; // full stop accepted, comma preferred // TODO: all well and good for a locale-specific decimal sign, but couldn''t the locale also upset microseconds formatting by grouping digits? os << std::put_time(&tm, self.basic ? "%Y%m%dT%H%M%S" : "%FT%T") << put_printf("%c%06i", decimal_sign, microseconds); if (! self.local) return os << "Z"; const int tz_minutes = std::abs(static_cast<int>(tm.tm_gmtoff)) / 60; return os << put_printf(self.basic ? "%c%02i%02i" : "%c%02i:%02i", 0 <= tm.tm_gmtoff ? ''+'' : ''-'', tz_minutes / 60, tz_minutes % 60); } }; int main() { // testing decimal sign ///std::cout.imbue(std::locale("en_GB")); ///std::cout.imbue(std::locale("fr_FR")); std::cout << put_timestamp() << " Hello, World!/n"; #if 0 typedef put_printf pf; // just to demo local abbreviation std::cout << "1: " << pf("%02147483646i/n" , 1 ) << std::endl; // res < 0 std::cout << "2: " << pf("%02147483643i%i/n", 1, 100) << std::endl; // res < 0 std::cout << "3: " << pf("%02147483643i%i/n", 1, 10) << std::endl; // works std::cout << "4: " << pf("%02147483646i" , 1 ) << std::endl; // works #endif return 0; }

Comentarios sobre put_printf:

// Reasons for the name "put_printf" (and not "putf" after all): // - put_printf is self-documenting, while using the naming pattern also seen in std::put_time; // - it is not clear whether the proposed std::putf would support exactly the same format syntax; // - it has a niche purpose, so a longer name is not an objection, and for frequent local uses // it is easy enough to declare an even shorter "typedef put_printf pf;" or so. // Evaluation of delegating to vsnprintf() with intermediate buffer: // (+) identical result without implementation and/or maintenance issues, // (?) succeeds or fails as a whole, no output of successful prefix before point of failure // (-) (total output size limited to INT_MAX-1) // (-) overhead (TODO: optimal buf_stack size considering cache and VM page locality?) // Error handling (an STL design problem?): // - std::cout.setstate(std::ios_base::failbit) discards further std::cout output (stdout still works), // so, to be aware of an error in business logic yet keep on trucking in diagnostics, // should there be separate classes, or a possibility to plug in an error handler, or what? // - should the basic or default error handling print a diagnostic message? throw an exception? // TODO: could a function "int ostream_printf(std::ostream& os, const char *format, ...)" // first try to write directly into os.rdbuf() before using buf_stack and buf_heap, // and would that significantly improve performance or not?


Sugiero usar ostringstream en lugar de ostream, vea el siguiente ejemplo:

#include <vector> #include <string> #include <iostream> #include "CppUnitTest.h" #define _CRT_NO_VA_START_VALIDATION std::string format(const std::string& format, ...) { va_list args; va_start(args, format); size_t len = std::vsnprintf(NULL, 0, format.c_str(), args); va_end(args); std::vector<char> vec(len + 1); va_start(args, format); std::vsnprintf(&vec[0], len + 1, format.c_str(), args); va_end(args); return &vec[0]; }

ejemplo de uso:

std::ostringstream ss; ss << format("%s => %d", "Version", Version) << std::endl; Logger::WriteMessage(ss.str().c_str()); // write to unit test output std::cout << ss.str() << std::endl; // write to standard output