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


当前回答

c++ 20有std::format,它在API方面类似于sprintf,但完全是类型安全的,适用于用户定义的类型,并使用类似python的格式字符串语法。下面是如何格式化std::string并将其写入流的方法:

std::string s = "foo";
std::cout << std::format("Look, a string: {}", s);

或者,你可以使用{fmt}库格式化字符串,并将其写入标准输出或文件流:

fmt::print("Look, a string: {}", s);

至于sprintf或这里的大多数其他答案,不幸的是,它们使用了可变参数,并且本质上是不安全的,除非您使用类似GCC的format属性,它只适用于文字格式字符串。你可以在下面的例子中看到为什么这些函数是不安全的:

std::string format_str = "%s";
string_format(format_str, format_str[0]);

其中string_format是Erik Aronesty的答案的实现。这段代码可以编译,但是当你试图运行它时,它很可能会崩溃:

$ g++ -Wall -Wextra -pedantic test.cc 
$ ./a.out 
Segmentation fault: 11

免责声明:我是{fmt}和c++ 20 std::format的作者。

其他回答

根据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。

这是我用来在我的程序中这样做的代码…这没什么特别的,但很管用……注意,您必须根据需要调整您的尺寸。我的MAX_BUFFER是1024。

std::string Format ( const char *fmt, ... )
{
    char textString[MAX_BUFFER*5] = {'\0'};

    // -- Empty the buffer properly to ensure no leaks.
    memset(textString, '\0', sizeof(textString));

    va_list args;
    va_start ( args, fmt );
    vsnprintf ( textString, MAX_BUFFER*5, fmt, args );
    va_end ( args );
    std::string retStr = textString;
    return retStr;
}

如果你在一个有asprintf(3)的系统上,你可以很容易地对它进行包装:

#include <iostream>
#include <cstdarg>
#include <cstdio>

std::string format(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));

std::string format(const char *fmt, ...)
{
    std::string result;

    va_list ap;
    va_start(ap, fmt);

    char *tmp = 0;
    int res = vasprintf(&tmp, fmt, ap);
    va_end(ap);

    if (res != -1) {
        result = tmp;
        free(tmp);
    } else {
        // The vasprintf call failed, either do nothing and
        // fall through (will return empty string) or
        // throw an exception, if your code uses those
    }

    return result;
}

int main(int argc, char *argv[]) {
    std::string username = "you";
    std::cout << format("Hello %s! %d", username.c_str(), 123) << std::endl;
    return 0;
}
inline void format(string& a_string, const char* fmt, ...)
{
    va_list vl;
    va_start(vl, fmt);
    int size = _vscprintf( fmt, vl );
    a_string.resize( ++size );
    vsnprintf_s((char*)a_string.data(), size, _TRUNCATE, fmt, vl);
    va_end(vl);
}

c++ 20 std::格式

它来了!该特性描述在:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0645r9.html,并使用类似python的.format()语法。

我希望它的用法是这样的:

#include <format>
#include <string>

int main() {
    std::string message = std::format("The answer is {}.", 42);
}

g++-9 -std=c++2a的GCC 9.1.0仍然不支持它。

现有的fmt库在获得官方支持之前实现了它:https://github.com/fmtlib/fmt,如前所述:std::string格式化,如sprintf:

sudo apt install libfmt-dev

修改源代码以替换:

用<fmt/core.h> .h格式化> Std::format转换为fmt::format

main.cpp

#include <string>
#include <iostream>

#include <fmt/core.h>

int main() {
    std::string message = fmt::format("The answer is {}.", 42);
    std::cout << message << std::endl;
}

编译并运行:

g++ -std=c++11 -o main.out main.cpp -lfmt
./main.out

输出:

The answer is 42.

API将添加一个新的std::format头文件:

建议的格式化API定义在新的header <format>中,应该不会对现有代码产生影响。

十六进制格式{:x}

c++十六进制成本值?

前导零{:03}

用c++输出操作符打印前导零?

左对齐{:<},右对齐{:>},中对齐{:^}

打印cout时c++对齐<<

浮点精度{:.2}

我如何打印一个双值与全精度使用cout? 在c++中设置默认浮点打印精度

在正数{:+}上显示符号

如何打印正数与前缀+在c++

将布尔值显示为true和false: {:}

在c++中将bool转换为文本