Cómo utilizar C++ std::ostream con printf-al igual que el formato?

Estoy aprendiendo C++. cout es una instancia de std::ostream clase.
¿Cómo puedo imprimir con formato de cadena con él?

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

  • esta página no es una guía completa para transmitir el formato Formato de Salida
  • Usted no debe realmente lo necesitan, porque puedes hacer cosas como cout << "my baby's age is " << 3 << endl; en lugar de printf("My baby's age is %u\n", 3);
  • cout << "my baby's age is " << 3 << endl; no es localizable; en lenguaje no-inglés puede tener diferentes palabras. Así, esta sintaxis no es aceptable para mutilanguage aplicaciones. La única manera de forzar esta sintaxis para trabajar es hacer switch que dependerá de IDENTIFICADOR de idioma, y esa solución es feo. printf es mucho mejor, porque el traductor puede traducir toda la cadena de formato y cambiar las palabras sin modificar el código fuente en cada idioma raro.
InformationsquelleAutor Eonil | 2013-02-27

7 Kommentare

  1. 33

    La única cosa que usted puede hacer con std::ostream directamente el bien conocido <<-sintaxis:

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

    Y hay varias IO manipuladores que pueden ser utilizados para influir en el formato, el número de dígitos, etc. de los números enteros, números de punto flotante, etc.

    Sin embargo, que no es el mismo que el formato de las cadenas de printf. C++11 no incluye ningún mecanismo que permite que usted utilice el formato de cadena de la misma manera como se utiliza con printf (excepto printf en sí, que por supuesto puede utilizar en C++ si quieres).

    En términos de bibliotecas que proporcionan printf-funcionalidad de estilo, hay boost::format, que permite que el 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 un propuesta para la inclusión de printf-formato de estilo en una futura versión de la Norma. Si esto es aceptado, una sintaxis como la siguiente puede ser que:

    std::cout << std::putf("this is a number: %d\n",i);
    • ¿Cómo sobre la cadena de generación? Ni ninguna forma de hacer objeto string con el formato?
    • Sí. Primero de todos, usted puede utilizar un std::ostringstream objeto de la misma manera como std::cout arriba. Cuando hayas rellenado con el contenido (usando el <<-operador), usted puede utilizar su .str() función para obtener la cadena con formato. Y boost::format devuelve una cadena de todos modos. Yo no la incluyen en la respuesta, ya que tu pregunta es acerca de std::cout específicamente.
    • Véase también fmtlib/fmt para una alternativa potente, C++ formato de la biblioteca.
  2. 4

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

    #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

    Esta un código muy simple y puede ser mejorado.

    1) La ventaja es que usa << para imprimir objetos en la secuencia, de modo que usted puede poner arbitrario de argumentos que pueden ser la salida a través de <<.

    2) ignora el tipo de argumento en la cadena con formato: después de % puede estar símbolo arbitrario incluso un espacio. El flujo de salida y decide cómo imprimir el objeto correspondiente. Es también compatible con printf.

    3) Una desventaja es que no se puede imprimir el símbolo de porcentaje ‘%’, en la necesidad de mejorar un poco el código.

    4) no se puede imprimir con formato de números, como %4.5 f

    5) Si el número de argumentos es menor que el predicho por el formato de cadena, la función de imprimir el resto de la cadena.

    6) Si el número de argumentos es mayor que el predicho por la cadena con formato, a continuación, el resto de argumentos son ignorados

    Se puede mejorar el código para hacer 2)-6) completamente de imitar el printf comportamiento.
    Sin embargo, si usted sigue las reglas de printf, a continuación, sólo 3) y 4) deben esencialmente a ser fijo.

  3. 3

    Ancho De Campo

    Ajuste de la anchura de campo es muy simple. Para cada variable, simplemente ir con «setw(n)». Como este:

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

    Observa cómo «setw(n)» controla el ancho de campo, por lo que cada número es
    impreso en el interior de un campo que se mantiene el mismo ancho independientemente de la
    la anchura de la cifra en sí.

    — De «Programación/C++ tutorial» por P. Lutus.

    • Si esta toda la respuesta es tomado del tutorial, usted debe indicar que con cita de formato.
  4. 1

    Ejemplo de salida:

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

    Código (con put_printf uso 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 acerca de 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?
    • Su código es ilegible. Por favor, abstracto, lejos de su aparente alergia a los espacios en blanco y saltos de línea.
  5. 1

    Me sugieren el uso de ostringstream en lugar de ostream
    ver 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
  6. 0

    Cuando me necesitan tanto la typesafety de cout y el fácil y rápido de formato de variables simples de printf(), mezclar los dos como este. Este es un feo revisión, pero consigue cosas hechas por mí cuando necesito de salida cosas como «02/07/2014 10:05am» junto con algunas más complejas entidades:

    #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;
    
    }
    • Utilizar al menos vsnprintf para evitar el tipo más obvio de error: el desbordamiento del búfer.
  7. 0

    Escribí de forma independiente, pero se acercó con respuesta similar a user3283405

    Mi solución utiliza vasprintf() para obtener el formato, y utiliza la sobrecarga de operadores de << de 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 en el interior de putf(), por lo que el compilador de la bandera -Wformat-no literal no advertir de código sospechoso en putf() y que usted necesita para cuidar incontrolada de la cadena de formato problema por ti mismo.

    La información detallada se puede encontrar en GitHub

Kommentieren Sie den Artikel

Bitte geben Sie Ihren Kommentar ein!
Bitte geben Sie hier Ihren Namen ein

Pruebas en línea