我必须用sprintf格式化std::string,并将其发送到文件流。我该怎么做呢?


当前回答

Windows和Visual Studio有一个非常有吸引力的解决方案:CString。

CString str;
str.Format("Hello %s\n", "World");
str = "ABC";
str += "DEF";

其他回答

我用vsnprintf写了我自己的,所以它返回字符串,而不是必须创建我自己的缓冲区。

#include <string>
#include <cstdarg>

//missing string printf
//this is safe and convenient but not exactly efficient
inline std::string format(const char* fmt, ...){
    int size = 512;
    char* buffer = 0;
    buffer = new char[size];
    va_list vl;
    va_start(vl, fmt);
    int nsize = vsnprintf(buffer, size, fmt, vl);
    if(size<=nsize){ //fail delete buffer and try again
        delete[] buffer;
        buffer = 0;
        buffer = new char[nsize+1]; //+1 for /0
        nsize = vsnprintf(buffer, size, fmt, vl);
    }
    std::string ret(buffer);
    va_end(vl);
    delete[] buffer;
    return ret;
}

所以你可以用它

std::string mystr = format("%s %d %10.5f", "omg", 1, 10.5);

这里是内存使用(和执行速度)方面的最佳解决方案,不依赖于RVO,如果字符串大小大于零,也可以执行追加,还会自动调整std::string的大小。

宏解决方案IMO更好,现代编译器将警告如果格式字符串不匹配的类型。该函数版本不会出现此警告,因为编译器无法看到snprintf。宏版本也更短,它也需要一个更少的包含。

来自:

https://github.com/ericcurtin/twincam

宏观的解决方案:

#include <string.h>
#include <string>

// function that will sprintf to a C++ string starting from std::string::size()
// so if you want to completely overwrite a string or start at a specific point
// use std::string::clear() or std::string::resize(). str is a std::string.
#define STRING_PRINTF(str, ...)                                   \
  do {                                                            \
    const int size = snprintf(NULL, 0, __VA_ARGS__);              \
    const size_t start_of_string = str.size();                    \
    str.resize(start_of_string + size);                           \
    snprintf(&str[start_of_string], str.size() + 1, __VA_ARGS__); \
  } while (0)

函数的解决方案:

#include <stdarg.h>  // For va_start, etc.
#include <string.h>
#include <string>

// function that will sprintf to a C++ string starting from std::string::size()
// so if you want to completely overwrite a string or start at a specific point
// use std::string::clear() or std::string::resize()
int string_printf(std::string& str, const char* const fmt, ...) {
  c_va_list c_args;

  va_start(c_args.args, fmt);

  c_va_list tmpa;
  va_copy(tmpa.args, c_args.args);

  // Get addtional size required
  int size = vsnprintf(NULL, 0, fmt, tmpa.args);
  if (size < 0) {
    return -1;
  }

  const size_t start_of_string = str.size();
  str.resize(start_of_string + size);

  // plus 1 so the null terminator gets included
  size = vsnprintf(&str[start_of_string], str.size() + 1, fmt, c_args.args);
  return size;
}

更优解:

#define STRING_PRINTF(str, ...)                                     \
  do {                                                              \
    const size_t write_point = str.size();                          \
    str.resize(write_point + 127);                                  \
    const int size = snprintf(&str[write_point], 128, __VA_ARGS__); \
    str.resize(write_point + size);                                 \
    if (size < 128) {                                               \
      break;                                                        \
    }                                                               \
                                                                    \
    snprintf(&str[write_point], size + 1, __VA_ARGS__);             \
  } while (0)

这是一个更优的解决方案,假设sprintf小于128字节,如果是,格式字符串只解析一次而不是两次。

你不能直接这样做,因为你没有对底层缓冲区的写访问权(直到c++ 11;见Dietrich Epp的评论)。你必须先在c-string中执行,然后将其复制到std::string中:

  char buff[100];
  snprintf(buff, sizeof(buff), "%s", "Hello");
  std::string buffAsStdStr = buff;

但我不确定为什么不直接使用字符串流?我想你有特定的理由不这么做:

  std::ostringstream stringStream;
  stringStream << "Hello";
  std::string copyOfStr = stringStream.str();

这是谷歌的做法: facebook也以类似的方式:StringPrintf (Apache许可证) 两者都提供了一个方便的StringAppendF。

根据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();
}

这避免了需要从原始答案中的.c_str()结果中取消const。