read whole lines from cin into vector

Hey,

here is a small problem i have a little trouble solving myself and finding hints or tipps for on the interwebs:
I want to use copy() from the STL to get input from cin directly to a vector of strings. However, I dont want cin to put single words into the vector, which is what the std extraction operator>> will do, but i want it to read complete lines. For that I will have to locally overwrite the opertator>> from the std namespace.
The following code:
1
2
3
4
5
6
7
8
9
10
11
12
int main()
{  
  vector<string> strVec;

  copy(istream_iterator<string>(cin), istream_iterator<string>(),  
                                          back_inserter(strVec));

  for(string str : strVec)
    {
      cout << str << '\n';
    }
}

given the input:
one two three
seven eight nine

should produce the exact same lines as output.

Up to now, this is meerly my understanding of the task, if something is completely bonkers in there, point it out and i will rethink the whole thing, but as it stands, this is what i m working on.

so, two problems:

i assume i use a anonymous namespace to overwrite operator>> for an istream and a string. i ve got the main from above in the same source file as the following definition:
1
2
3
4
5
6
7
namespace
{
  std::istream &operator>>(std::istream &istr, std::string &str)
  {
    getline(istr, str);
  }
}

neglecting the actual implementation, is this the correct apporach?

2nd problem:
i am unsure as to what exactly i have to redefine and in what manner. can i modify the argument str from within this function?

Any advice, hints, tips are much appreciated
thanks, jaqq
I think that it is a bad idea.

It would be better to write simply

std::string s;
while ( std::getline( std::cin, s ) ) strVec.push_back();
unfortunatley, that is a 100% not allowed, cant define any variables. 0. using as much stl as possible
So strvec and str in your code are not variables?

You do know that std::getline and std::string are just as much a part of the library as std::vector and std::copy?
Last edited on
yea do,
however i did not declare a variable in that main function and am not allowed to. it feels a little contrived to me, too, but im 100% affirmative on that part of the exercise.
i would be very much interested if using an anonymous namespace in that fashion is possible.
also, i still dont 100% get how to overwrite the operator. i cant really test it out, because i dont even know if it gets used or not.
i would be very much interested if using an anonymous namespace in that fashion is possible.

The most you could do was make the call ambiguous so the code wouldn't compile.

i cant really test it out, because i dont even know if it gets used or not.

/me facepalm

If the fact that your tokens are still individual words when you try this doesn't clue you in, you might consider trying the following:

1
2
3
4
5
6
7
8
namespace
{
  std::istream &operator>>(std::istream &istr, std::string &str)
  {
    std::cout << "HERE I AM!\n";
    return getline(istr, str);
  }
}


Think you'd notice if that got used?
I want to use copy() from the STL to get input from cin directly to a vector of strings. However, I dont want cin to put single words into the vector, which is what the std extraction operator>> will do, but i want it to read complete lines


There are a few ways to do that, but no, declaring your own operator>> for standard strings is not going to work, in unnamed namespace or not.

The closest to your approach is declaring your own operator>> for your own class that is convertible to string:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <iterator>
using namespace std;

class line {
    std::string data;
 public:
     friend std::istream &operator>>(std::istream &is, line &l) {
              return std::getline(is, l.data);
     }
     operator std::string() const { return data; }
};

int main()
{
  vector<string> strVec;

  copy(istream_iterator<line>(cin), istream_iterator<line>(),
                                          back_inserter(strVec));

  for(string str : strVec)
    {
      cout << str << '\n';
    }
}



Another way to do this is to update the character classification table in std::cin so that it doesn't see spaces (and tabs, for completeness) as whitespace:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
#include <algorithm>
#include <vector>
#include <iterator>
#include <locale>
using namespace std;

struct lines_only : ctype<char> {
    static const mask* make_table()
    {
        static vector<mask> v(classic_table(), classic_table() + table_size);
        v[' '] &= ~space;  // space will not be classified as whitespace
        v['\t'] &= ~space; // tab will not be classified as whitespace
        return &v[0];
    }
    lines_only(size_t refs = 0) : ctype(make_table(), false, refs) {}
};

int main()
{
  vector<string> strVec;

  cin.imbue(locale(cin.getloc(), new lines_only()));

  copy(istream_iterator<string>(cin), istream_iterator<string>(),
                                          back_inserter(strVec));

  for(string str : strVec)
    {
      cout << str << '\n';
    }
}


Another way to do it is to build a line input iterator, which would be very similar to the standard stream input iterator, except it would call getline() in its operator++

Last edited on
Topic archived. No new replies allowed.