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


当前回答

到目前为止,所有的答案似乎都有一个或多个这样的问题:(1)它可能无法在vc++上工作(2)它需要额外的依赖,如boost或fmt(3)它太复杂的自定义实现,可能没有经过很好的测试。

下面的代码解决了上述所有问题。

#include <string>
#include <cstdarg>
#include <memory>

std::string stringf(const char* format, ...)
{
    va_list args;
    va_start(args, format);
    #ifndef _MSC_VER

        //GCC generates warning for valid use of snprintf to get
        //size of result string. We suppress warning with below macro.
        #ifdef __GNUC__
        #pragma GCC diagnostic push
        #pragma GCC diagnostic ignored "-Wformat-nonliteral"
        #endif

        size_t size = std::snprintf(nullptr, 0, format, args) + 1; // Extra space for '\0'

        #ifdef __GNUC__
        # pragma GCC diagnostic pop
        #endif

        std::unique_ptr<char[]> buf(new char[ size ] ); 
        std::vsnprintf(buf.get(), size, format, args);
        return std::string(buf.get(), buf.get() + size - 1 ); // We don't want the '\0' inside
    #else
        int size = _vscprintf(format, args);
        std::string result(++size, 0);
        vsnprintf_s((char*)result.data(), size, _TRUNCATE, format, args);
        return result;
    #endif
    va_end(args);
}    

int main() {
    float f = 3.f;
    int i = 5;
    std::string s = "hello!";
    auto rs = stringf("i=%d, f=%f, s=%s", i, f, s.c_str());
    printf("%s", rs.c_str());
    return 0;
}

注:

Separate VC++ code branch is necessary because VC++ has decided to deprecate snprintf which will generate compiler warnings for other highly voted answers above. As I always run in "warnings as errors" mode, its no go for me. The function accepts char * instead of std::string. This because most of the time this function would be called with literal string which is indeed char *, not std::string. In case you do have std::string as format parameter, then just call .c_str(). Name of the function is stringf instead of things like string_format to keepup with printf, scanf etc. It doesn't address safety issue (i.e. bad parameters can potentially cause seg fault instead of exception). If you need this then you are better off with boost or fmt libraries. My preference here would be fmt because it is just one header and source file to drop in the project while having less weird formatting syntax than boost. However both are non-compatible with printf format strings so below is still useful in that case. The stringf code passes through GCC strict mode compilation. This requires extra #pragma macros to suppress false positives in GCC warnings.

以上代码已在,

GCC 4.9.2 11 / c++ / C + + 14 vc++编译器19.0 铿锵声3.7.0

其他回答

String没有你需要的东西,但是std::stringstream有。使用stringstream创建字符串,然后提取字符串。这里有一个关于你可以做的事情的全面列表。例如:

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

将在打印双精度或浮点数时提供10位小数点后的精度。

这是可以尝试的。简单。虽然没有使用字符串类的细微差别。

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include <string>
#include <exception>
using namespace std;

//---------------------------------------------------------------------

class StringFormatter
{
public:
    static string format(const char *format, ...);
};

string StringFormatter::format(const char *format, ...)
{
    va_list  argptr;

    va_start(argptr, format);

        char   *ptr;
        size_t  size;
        FILE   *fp_mem = open_memstream(&ptr, &size);
        assert(fp_mem);

        vfprintf (fp_mem, format, argptr);
        fclose (fp_mem);

    va_end(argptr);

    string ret = ptr;
    free(ptr);

    return ret;
}

//---------------------------------------------------------------------

int main(void)
{
    string temp = StringFormatter::format("my age is %d", 100);
    printf("%s\n", temp.c_str());

    return 0;
}

到目前为止,所有的答案似乎都有一个或多个这样的问题:(1)它可能无法在vc++上工作(2)它需要额外的依赖,如boost或fmt(3)它太复杂的自定义实现,可能没有经过很好的测试。

下面的代码解决了上述所有问题。

#include <string>
#include <cstdarg>
#include <memory>

std::string stringf(const char* format, ...)
{
    va_list args;
    va_start(args, format);
    #ifndef _MSC_VER

        //GCC generates warning for valid use of snprintf to get
        //size of result string. We suppress warning with below macro.
        #ifdef __GNUC__
        #pragma GCC diagnostic push
        #pragma GCC diagnostic ignored "-Wformat-nonliteral"
        #endif

        size_t size = std::snprintf(nullptr, 0, format, args) + 1; // Extra space for '\0'

        #ifdef __GNUC__
        # pragma GCC diagnostic pop
        #endif

        std::unique_ptr<char[]> buf(new char[ size ] ); 
        std::vsnprintf(buf.get(), size, format, args);
        return std::string(buf.get(), buf.get() + size - 1 ); // We don't want the '\0' inside
    #else
        int size = _vscprintf(format, args);
        std::string result(++size, 0);
        vsnprintf_s((char*)result.data(), size, _TRUNCATE, format, args);
        return result;
    #endif
    va_end(args);
}    

int main() {
    float f = 3.f;
    int i = 5;
    std::string s = "hello!";
    auto rs = stringf("i=%d, f=%f, s=%s", i, f, s.c_str());
    printf("%s", rs.c_str());
    return 0;
}

注:

Separate VC++ code branch is necessary because VC++ has decided to deprecate snprintf which will generate compiler warnings for other highly voted answers above. As I always run in "warnings as errors" mode, its no go for me. The function accepts char * instead of std::string. This because most of the time this function would be called with literal string which is indeed char *, not std::string. In case you do have std::string as format parameter, then just call .c_str(). Name of the function is stringf instead of things like string_format to keepup with printf, scanf etc. It doesn't address safety issue (i.e. bad parameters can potentially cause seg fault instead of exception). If you need this then you are better off with boost or fmt libraries. My preference here would be fmt because it is just one header and source file to drop in the project while having less weird formatting syntax than boost. However both are non-compatible with printf format strings so below is still useful in that case. The stringf code passes through GCC strict mode compilation. This requires extra #pragma macros to suppress false positives in GCC warnings.

以上代码已在,

GCC 4.9.2 11 / c++ / C + + 14 vc++编译器19.0 铿锵声3.7.0

你不能直接这样做,因为你没有对底层缓冲区的写访问权(直到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();

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

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