如何使用类似printf格式的C++ std :: ostream?

Eon*_*nil 29 c++ formatting printf ostream

我正在学习C++.cout是一个std::ostream类的实例.如何用它打印格式化的字符串?

我仍然可以使用printf,但我想学习更多C++风格的方法,它可以带来所有C++的好处.我认为这应该是可能的,std::ostream但我找不到合适的方法.

jog*_*pan 32

您可以std::ostream直接做的唯一事情是众所周知的<<-syntax:

int i = 0;
std::cout << "this is a number: " << i;
Run Code Online (Sandbox Code Playgroud)

并且有各种IO操纵器可用于影响整数,浮点数等的格式,位数等.

但是,这与格式化的字符串不同printf.C++ 11不包含任何允许您以与使用它相同的方式使用字符串格式的工具printf(除了printf本身,如果您愿意,您当然可以在C++中使用它).

在提供printf样式功能的库方面boost::format,可以实现这样的代码(从概要中复制):

std::cout << boost::format("writing %1%,  x=%2% : %3%-th try") % "toto" % 40.23 % 50;
Run Code Online (Sandbox Code Playgroud)

还要注意的是有一个列入提议printf在标准的未来版本样式的格式.如果这被接受,则可能会出现以下语法:

std::cout << std::putf("this is a number: %d\n",i);
Run Code Online (Sandbox Code Playgroud)

  • 为'std :: putf`提案链接+1!很高兴见到这一点. (7认同)
  • @Eonil是的.首先,您可以像上面的`std :: cout`一样使用[`std :: ostringstream`](http://en.cppreference.com/w/cpp/io/basic_stringstream)对象.当你用内容填充它时(使用```-operator),可以使用它的`.str()`函数来获取格式化的字符串.并且`boost :: format`无论如何都会返回一个字符串.我没有在答案中包含这个,因为你的问题是关于`std :: cout`的具体问题. (2认同)
  • 另请参阅[`fmtlib / fmt`](https://github.com/fmtlib/fmt),以获取替代的功能强大的C ++格式库。 (2认同)

vit*_*aut 15

在 C++20 中,您将能够使用类似std::format安全printf的格式:

std::cout << std::format("The answer is {}.\n", 42);
Run Code Online (Sandbox Code Playgroud)

除了该{} FMT库std::format是基于,提供了print结合的格式化和输出功能:

fmt::print("The answer is {}.\n", 42);
Run Code Online (Sandbox Code Playgroud)

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

  • 我们正在努力,希望 std::print 能够进入 C++23。 (4认同)
  • 感谢您在这里所做的工作。我总是对使用左移运算符进行 IO 的概念感到困惑。C 有很多 C++ 希望解决的问题,但当他们完全抛弃格式字符串时,他们真的把婴儿和洗澡水一起泼了出去(是的,我知道 printf 一直都在那里,但编译器本身_判断_你在 C++ 中使用 libc )。 (2认同)
  • 似乎很遗憾“fmt::print()”没有标准化。`fmt` 很棒:灵活、简单、安全和快速(特别是对于像浮动一样的东西,现在有 Dragonbox(?) 在引擎盖下)。然后我们必须 `std::cout &lt;&lt; std::format(...);` ?? 我做了一个简单的测试,`fmt::print(...)` 立刻就快了 10-15%(还有 `fmt::print(std::FILE,..)` 用于其他任何事情)。那就是“std::ios::sync_with_stdio(false);” 有什么解决方案吗? (2认同)

小智 11

这是我已经习惯的成语。希望它有帮助:

// Hacky but idiomatic printf style syntax with c++ <<

#include <cstdlib> // for sprintf

char buf[1024]; sprintf(buf, "%d score and %d years ago", 4, 7);
cout << string(buf) <<endl;
Run Code Online (Sandbox Code Playgroud)

&

  • Ofc 在你发布代码之前不会溢出,对吧哈哈所以最好使用更安全的东西,比如_snprintf_(有警告:请参阅/sf/answers/1015274011/) (3认同)
  • 对我来说这是最好的。熟悉上下文,并将我的 C 代码简单更改为 C++。它使用“cstdlib”来实现其预期目的。 (2认同)

ser*_*rup 8

我建议使用 ostringstream 而不是 ostream 参见以下示例:

#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];
}
Run Code Online (Sandbox Code Playgroud)

用法示例:

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
Run Code Online (Sandbox Code Playgroud)

  • 这是我认为最好的答案。使用 vsnprintf 获取“将要写入的字符数”,但不允许它写入任何内容是天才。我进行了一项修改...我直接使用字符数组而不是向量(“char vec[len + 1];”)。认为这样的开销会更少。然后返回字符数组(`return vec;`)。 (2认同)

Ser*_*nko 5

要实现 printf 可以使用 c++11 模板参数:

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

}
Run Code Online (Sandbox Code Playgroud)

输出

300 + 2 = %a 
message: "Hello!". 
2 + 12
Run Code Online (Sandbox Code Playgroud)

这是一个非常简单的代码,可以改进。

1)优点是它使用<<将对象打印到流,因此您可以放置​​可以通过<<输出的任意参数。

2) 它忽略格式化字符串中参数的类型:在 % 之后可以代表任意符号,甚至是空格。输出流决定如何打印相应的对象。它还与 printf 兼容。

3)缺点是不能打印百分号'%',需要稍微改进一下代码。

4) 它不能打印格式化的数字,如 %4.5f

5) 如果参数数量少于格式化字符串预测的数量,则该函数只打印字符串的其余部分。

6) 如果参数数量大于格式化字符串预测的数量,则忽略剩余的参数

可以改进代码使 2)-6) 完全模仿 printf 行为。但是,如果您遵循 printf 的规则,那么基本上只需要修复 3) 和 4)。