【问题标题】:Remove input from an output stream从输出流中删除输入
【发布时间】:2013-11-06 16:10:57
【问题描述】:

我想使用std::copystd::ostream_iteratorvector 中的字符串打印为括号之间的逗号分隔列表。所以我必须处理在最后一个元素之后删除", "的问题。

我试过this:

#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <iterator>

int main() {

    std::vector<std::string> v;
    v.push_back("a");
    v.push_back("b");
    v.push_back("c");

    std::stringstream os;
    os << '(';
    std::copy(v.begin(), v.end(), std::ostream_iterator<std::string>(os, ", "));
    os.seekp(-2, std::ios_base::cur);
    os << ')';

    std::cout << os.str() << ".";

    return 0;
}

输出:

(a, b, c) .
         ^

但是我得到的输出在右括号之后有一个额外的空白字符,因为seekp 只是移动了书写位置,但没有删除书写的字符。

有没有办法删除最后一个字符或写入 EOF?

【问题讨论】:

  • 您是否愿意接受替代解决方案,或者您是否会接受唯一的选择?
  • @Benjamin Lindley:我很开放

标签: c++ vector iostream ostream


【解决方案1】:

一开始就不要在最后写额外的", " 怎么样...

if(!v.empty())
{
   std::copy(v.begin(), v.end()-1, std::ostream_iterator<std::string>(os, ", "));
   os << v.back();
}

【讨论】:

  • 值得注意的是,此解决方案适用于vector,但不适用于set,我还想为此实现类似的打印功能。由于性能不是问题,我将只从set 创建一个临时的vector 并重用vector 打印功能。
  • @AntonioPérez:将v.end()-1 替换为std::prev(v.end()),将v.back() 替换为*std::prev(v.end()),它适用于集合。
  • @AntonioPérez:std::prev 是一个非常简单的函数,您可以自己实现,或者简单地实现内联逻辑。只需从v.end() 创建一个迭代器,然后递减一次。
【解决方案2】:

当您使用seekp 时,您不只是删除字符,而是设置当前光标。您的下一条语句将覆盖该字符,但缓冲区中仍有后续字符。有几种方法可以解决这个问题:

不要输出完整的模式:

std::ostringstream os;
os << '(';
std::copy(v.begin(), v.end() - 1, std::ostream_iterator<std::string>(os, ", "));
if (v.size() > 1) // or (!v.empty())
    os << *(v.end() - 1) << ')';

这将使用模式打印除向量的最后一个字符之外的所有字符(将完全避免额外的“,”组合)。

或者,您可以手动重置缓冲区:

std::ostringstream os;
os << '(';
std::copy(v.begin(), v.end(), std::ostream_iterator<std::string>(os, ", "));
std::string t = os.str();
t.resize(t.size() - 2);
os.str() = t;
os << ')';

或者,也许是更统一的解决方案(如果您不反对不使用副本):

std::ostringstream os;
os << '(';
if (v.size() > 1) // or (!v.empty())
    os << v.front();

std::for_each(v.begin() + 1, v.end(), [&](const std::string& s)
{
    os << ", " << s;
});
os << ")";

【讨论】:

  • 你的上一个提案看起来很不错。可惜我的编译器不支持 C++11 特性。
  • 您可以在仿函数中执行此操作或编写自己的循环,它看起来不会有太大不同(并且可以在 C++98/03 中编译)。
  • 第二个提案仅适用于stringstream,不适用于ostream。我只是在示例中使用了stringstream,但我的实际代码涵盖了使用ostream 的一般情况。
  • @AntonioPérez 你的意思是重置字符串的那个?是的,这是特定于ostringstream 的。就个人而言,我会推荐第三种方法。一般来说,我会尽量避免将多余的字符写入字符串,然后再修复它。它只是使代码的可读性降低。
猜你喜欢
  • 2015-12-30
  • 1970-01-01
  • 2015-05-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-04-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多