std::string formatting like sprintf

前端 未结 30 2603
野趣味
野趣味 2020-11-22 04:42

I have to format std::string with sprintf and send it into file stream. How can I do this?

相关标签:
30条回答
  • 2020-11-22 05:12

    There can be problems, if the buffer is not large enough to print the string. You must determine the length of the formatted string before printing a formatted message in there. I make own helper to this (tested on Windows and Linux GCC), and you can try use it.

    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();
    
    0 讨论(0)
  • 2020-11-22 05:13

    Based on the answer provided by 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();
    }
    

    This avoids the need to cast away const from the result of .c_str() which was in the original answer.

    0 讨论(0)
  • 2020-11-22 05:14

    My two cents on this very popular question.

    To quote the manpage of printf-like functions:

    Upon successful return, these functions return the number of characters printed (excluding the null byte used to end output to strings).

    The functions snprintf() and vsnprintf() do not write more than size bytes (including the terminating null byte ('\0')). If the output was truncated due to this limit then the return value is the number of characters (excluding the terminating null byte) which would have been written to the final string if enough space had been available. Thus, a return value of size or more means that the output was truncated.

    In other words, a sane C++11 implementation should be the following:

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

    It works quite well :)

    Variadic templates are supported only in C++11. The answer from pixelpoint show a similar technique using older programming styles.

    It's weird that C++ does not have such a thing out of the box. They recently added to_string(), which in my opinion is a great step forward. I'm wondering if they will add a .format operator to the std::string eventually...

    Edit

    As alexk7 pointed out, A +1 is needed on the return value of std::snprintf, since we need to have space for the \0 byte. Intuitively, on most architectures missing the +1 will cause the required integer to be partially overwritten with a 0. This will happen after the evaluation of required as actual parameter for std::snprintf, so the effect should not be visible.

    This problem could however change, for instance with compiler optimization: what if the compiler decides to use a register for the required variable? This is the kind of errors which sometimes result in security issues.

    0 讨论(0)
  • 2020-11-22 05:16

    I realize this has been answered many times, but this is more concise:

    std::string format(const std::string fmt_str, ...)
    {
        va_list ap;
        char *fp = NULL;
        va_start(ap, fmt_str);
        vasprintf(&fp, fmt_str.c_str(), ap);
        va_end(ap);
        std::unique_ptr<char[]> formatted(fp);
        return std::string(formatted.get());
    }
    

    example:

    #include <iostream>
    #include <random>
    
    int main()
    {
        std::random_device r;
        std::cout << format("Hello %d!\n", r());
    }
    

    See also http://rextester.com/NJB14150

    0 讨论(0)
  • 2020-11-22 05:17

    string doesn't have what you need, but std::stringstream does. Use a stringstream to create the string and then extract the string. Here is a comprehensive list on the things you can do. For example:

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

    will give you 10 decimal places of precision when printing a double or float.

    0 讨论(0)
  • 2020-11-22 05:17

    You could try this:

    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 );
    
    0 讨论(0)
提交回复
热议问题