19

I have a text in a std::string object. The text consists of several lines. I want to iterate over the text line by line using STL (or Boost). All solutions I come up with seem to be far from elegant. My best approach is to split the text at the line breaks. Is there a more elegant solution?

UPDATE: This is what I was looking for:

std::string input;
// get input ...
std::istringstream stream(input);
std::string line;
while (std::getline(stream, line)) {
  std::cout << line << std::endl;
}
Jan Deinhard
  • 19,645
  • 24
  • 81
  • 137
  • Are you looking for "more elegant" like C#'s foreach "foreach( string line in lines)"? What exactly have you tried. C++ could feel 'inelegant' if you're used to dynamic or higher level languages, but in C++ "elegant" is never just handed to you, you usually have to program elegance yourself. – Chris Pfohl Nov 16 '10 at 13:43
  • 1
    It might not be *quite* an exact duplicate, but you might also want to look at: http://stackoverflow.com/questions/1567082/how-do-i-iterate-over-cin-line-by-line-in-c – Jerry Coffin Nov 16 '10 at 14:23
  • It would be better to answer your own question instead of updating it. Updating may be confusing for future reader... – Wolf Mar 17 '14 at 14:58

3 Answers3

18

Why do you keep the text in your source file? Keep it in a separate text file. Open it with std::ifstream and iterate over it with while(getline(...))

#include <iostream>
#include <fstream>

int main()
{
   std::ifstream  fin("MyText.txt");
   std::string    file_line;
   while(std::getline(fin, file_line))
   {
      //current line of text is in file_line, not including the \n 
   }
}

Alternatively, if the text HAS to be in a std::string variable read line by line using std::istringstream in a similar manner

If your question is how to put the text lexially into your code without using +, please note that adjacent string literals are concatenated before compilation, so you could do this:

std::string text = 
   "Line 1 contents\n"
   "Line 2 contents\n"
   "Line 3 contents\n";
giuspen
  • 1,365
  • 3
  • 20
  • 33
Armen Tsirunyan
  • 130,161
  • 59
  • 324
  • 434
  • 5
    If you really need to work with a `std::string`, you can wrap it in a `std::stringstream` and apply Armen's solution. – Björn Pollex Nov 16 '10 at 13:37
  • If the data is already a string, you could just use `std::stringstream` in the same manner. – Chris Lutz Nov 16 '10 at 13:38
  • @Armen: The text is going to be fetched from the web. Of course I could write it into a file and then read it again. I came up with that solution too. I'd rather have it in memory all the time ... if possible :) – Jan Deinhard Nov 16 '10 at 13:42
  • 1
    @Fair: I don't get it, really. How are you reading the text from the web? Does the text already contain line breaks? If so, then I don't see the problem, if not, then maybe you want to add them wherever you think appropriate. Also note that getline can take a third parameter - the character that denotes the end of line. Default is '\n' but you can change that – Armen Tsirunyan Nov 16 '10 at 13:45
  • @Armen: Thanks for the pointer! The std::istringstream variant is the "elegance" I was looking for. By the way: I read the text from the web with libcurl. – Jan Deinhard Nov 16 '10 at 14:00
10

Use Boost.Tokenizer:

std::string text("foo\n\nbar\nbaz");

typedef boost::tokenizer<boost::char_separator<char> > line_tokenizer;
line_tokenizer tok(text, boost::char_separator<char>("\n\r"));

for (line_tokenizer::const_iterator i = tok.begin(), end = tok.end();
     i != end ; ++i)
    std::cout << *i << std::endl;

prints

foo
bar
baz

Note that it skips over empty lines, which may or may not be what you want.

Fred Foo
  • 355,277
  • 75
  • 744
  • 836
  • Isn't using boost::tokenizer for something that can be done with getline sort-of like killing a sparrow with a nuke? – Armen Tsirunyan Nov 16 '10 at 13:50
  • Depends on what the OP wants to do. This solution is quite short and it yields a pair of iterators that can be handed to STL algorithms. – Fred Foo Nov 16 '10 at 13:53
3

If you want to loop line by line, as you say, why would splitting the text at line breaks not be exactly what you want?

You didn't post code showing how you're doing it, but your approach seems correct to accomplish what you said you wanted. Why does it feel inferior?

KevenK
  • 2,975
  • 3
  • 26
  • 33
  • I'd rather use some STL build-in solution. AFAIK line endings are platform-dependent so I'd like to avoid that solution if possible. – Jan Deinhard Nov 16 '10 at 13:39
  • @Fair: That (along with your current code) seems like information that should have been in the original question. It's a valid concern and can be addressed, but only if you address it head-on :) – KevenK Nov 16 '10 at 13:42
  • @Fair - I'm not sure about C++, but C stdio functions convert `\r\n` to `\n` on reading (unless told not to) and `\n` to `\r\n` on writing on systems that don't play nice. Line ending platform-dependency is a small thing to be worried about here. – Chris Lutz Nov 16 '10 at 13:43
  • To be honest I don't really have working code yet. I made some solutions up in my mind :) But I'll add some code in a moment. – Jan Deinhard Nov 16 '10 at 13:45