Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Remove input from an output stream

I want to use std::copy and std::ostream_iterator to print out the string within a vector as a coma separated list between parenthesis. So I have to deal with the issue of removing the ", " after the last element.

I have tried 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;
}

Output:

(a, b, c) .
         ^

But the output I get has an extra white space character after the closing parenthesis, because seekp just moves the writing position but doesn't remove the written characters.

Is there a way to remove that last character or write an EOF?

like image 268
Antonio Pérez Avatar asked May 31 '26 18:05

Antonio Pérez


2 Answers

How about not writing the extra ", " at the end in the first place...

if(!v.empty())
{
   std::copy(v.begin(), v.end()-1, std::ostream_iterator<std::string>(os, ", "));
   os << v.back();
}
like image 103
David Avatar answered Jun 03 '26 06:06

David


You are not just removing the characters when you use seekp, you are setting the current cursor. Your next statement will overwrite that character, but you still have the subsequent characters in the buffer already. There are a couple of ways to deal with that:

Don't output the full pattern:

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

This will print all but the last character of your vector with the pattern (the extra ", " combination will be avoided entirely).

Alternatively, you can reset the buffer manually:

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

Or, perhaps a more uniform solution (if you are not opposed to not using copy):

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 << ")";
like image 31
Zac Howland Avatar answered Jun 03 '26 06:06

Zac Howland