I'm reading some network data into a stringstream as an input_buffer.
The data is ASCII lines separated by a LF char.
The input_buffer may be in a state where there is only a partial line in it.
I'm trying to call getline ()
, but only when there actually is a new newline char in the stringstream. In other words it should extract completed lines, but leave a partial line in the buffer.
Here is a MVCE:
#include <string>
#include <sstream>
#include <iostream>
int
main (void)
{
std::stringstream input_buffer;
input_buffer << "test123\nOK\n";
while (input_buffer.str ().find ('\n') != std::string::npos)
{
std::string line;
std::getline (input_buffer, line, '\n');
std::cout << "input_buffer.str ().size: " << input_buffer.str ().size () << "\n";
std::cout << "line: " << line << "\n";
}
return 0;
}
It currently does not terminate, here is a fragment of the output:
input_buffer.str ().size: 11
line: test123
input_buffer.str ().size: 11
line: OK
input_buffer.str ().size: 11
line:
input_buffer.str ().size: 11
...
How can I read a line from a stringstream only if it contains any newline?
Edit: For clarification here is another code sample with partial input:
#include <string>
#include <sstream>
#include <iostream>
#include <vector>
void
extract_complete_lines_1 (std::stringstream &input_buffer, std::vector<std::string> &lines)
{
while (input_buffer.str ().find ('\n') != std::string::npos)
{
std::string line;
std::getline (input_buffer, line, '\n');
lines.push_back (line);
}
}
void
print_lines (const std::vector<std::string> &v)
{
for (auto l : v)
{
std::cout << l << '\n';
}
}
int
main (void)
{
std::vector<std::string> lines;
std::stringstream input_buffer {"test123\nOK\npartial line"};
extract_complete_lines_1 (input_buffer, lines);
print_lines (lines);
return 0;
}
This should print "test123" and "OK", but not "partial line".
I think that it's not easily possible with std::stringstream
. I tried to manipulate the stream position with tellg ()
and seekg ()
, but they don't behave like I expected.
I have found a solution using a std::vector<char>
as a buffer:
#include <string>
#include <sstream>
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
void
extract_complete_lines (std::vector<char> &buf, std::vector<std::string> &lines)
{
auto pos = std::end (buf);
while ((pos = std::find (std::begin (buf), std::end (buf), '\n')) != std::end (buf))
{
std::string line (std::begin (buf), pos);
buf.erase (std::begin(buf), pos + 1);
lines.push_back (line);
}
}
void
print_lines (const std::vector<std::string> &v)
{
for (auto l : v)
{
std::cout << l << '\n';
}
}
int
main (void)
{
std::vector<std::string> lines;
const std::string test_input = "test123\nOK\npartial line";
std::vector<char> input_buffer {std::begin (test_input), std::end (test_input)};
extract_complete_lines_1 (input_buffer, lines);
print_lines (lines);
return 0;
}
It prints the first two lines as expected and the "partial line" is left in the vector.
Or even better, a std::vector<char>
is not too different from a std::string
:
#include <string>
#include <sstream>
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
void
extract_complete_lines (std::string &buf, std::vector<std::string> &lines)
{
std::string::size_type pos;
while ((pos = buf.find ('\n')) != std::string::npos)
{
lines.push_back (buf.substr (0, pos));
buf.erase (0, pos + 1);
}
}
void
print_lines (const std::vector<std::string> &v)
{
for (auto l : v)
{
std::cout << l << '\n';
}
}
int
main (void)
{
std::vector<std::string> lines;
std::string input_buffer = "test123\nOK\npartial line";
extract_complete_lines (input_buffer, lines);
print_lines (lines);
return 0;
}