【问题标题】:Get the size of a an element in a C++ output stream获取 C++ 输出流中元素的大小
【发布时间】:2019-09-25 10:03:50
【问题描述】:

我正在格式化一些日志输出。我希望最终结果如下所示:

Foo.................................12.1
Bar....................................4
Foo Bar............................42.01

总宽度是恒定的,但两个参数(名称和值)都有不同的大小。一旦包含在 std::ostream 中,是否有一种干净的方法来获取给定参数的宽度?

#include <iostream>

struct Entry {
    std::string name_;
    double value_;
};

constexpr int line_width = 30;

std::ostream& operator<<(std::ostream& log, const Entry& e)
{
    log << e.name_
        << std::string(line_width - e.name_.size(), '.') \\ should subtract the width of e.value_
        << e.value_;
    return log;
}

int main()
{
    Entry foo    = { "Foo", 12.1 };
    Entry bar    = { "Bar", 4};
    Entry foobar = { "Foo Bar", 42.01};

    std::cout << foo << '\n' << bar << '\n' << foobar << '\n';
}

上面的代码不起作用,因为我没有减去值的宽度。我正在考虑编写一个可以执行以下操作的函数:

template <typename T>
int get_width(std::ostream& log, T value)
{
    // 1. use tellp to get initial stream size
    // 2. insert the value in the stream
    // 3. use tellp to get final stream size  
    // 4. remove the value from the stream (is that even possible?)
    // 5. return the size = final - initial 
}

有没有一种干净的方法可以达到我的目标?

【问题讨论】:

  • name_value_的最大输出长度是否提前知道?还是需要从数据中计算出这两个宽度的最大值,然后用它来确定布局?
  • Value 是用户指定的双精度值。从技术上讲,它可以包含 15 位数字,但实际上人们使用 3 或 4。

标签: c++ stream ostream


【解决方案1】:

正如发布的那样,这个问题有点像 X-Y 问题,因为您不需要知道宽度即可获得所需的输出。为此,知道该字段的总width 就足够了,并使用合适的fill 字符。

这应该适合你:

std::ostream& operator<<(std::ostream& log, const Entry& e)
{
    auto beg = log.tellp();
    log << e.name_;
    auto len = log.tellp() - beg;
    auto oldFill = log.fill();
    auto oldWidth = log.width();
    log.fill('.');
    log.width(line_width - len);
    log << e.value_;
    log.fill(oldFill);
    log.width(oldWidth);
    return log;
}

[Live example]

请注意,这依赖于流实际上能够通过tellp() 报告有效值。基于文件的流是; std::cout 连接的终端没有。

【讨论】:

  • 虽然这段代码可以完成这项工作,但它确实不是很有表现力/干净的恕我直言。如果其他开发人员查看这几行代码,他将不知道发生了什么。我可以添加 cmets,但我宁愿首先有一个“更清洁”的代码。有没有办法封装其中的一些?这只会在代码中调用一次,因此性能在这里并不是很重要。
【解决方案2】:

您可以创建一个包含“名称”的字符串,以及一个包含“值”的字符串。

然后很容易计算这两个字符串的总长度,并由此计算它们之间所需的空间。

【讨论】:

  • 如果我这样做,它不会遵循我的输出流的设置,对吧?
  • @Touloudou 这真的取决于。您可以使用 std::ostringstream 为数字创建字符串,并为此使用标准 I/O 操纵器。还是你的意思是别的?你能详细说明一下吗?
  • 我必须将我的行添加到具有自定义格式(精度等)的现有输出流中。我需要确保新创建的字符串以完全相同的方式格式化。
  • @Touloudou 您可以获取为输出流设置的标志和选项,并将它们用于字符串流。
  • 我想我会同意你最初的想法。它是一个记录器,所以我想格式是否不完全相同并不重要。使用标志会使函数变得更复杂,但收益甚微。
猜你喜欢
  • 1970-01-01
  • 2012-06-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多