fork download
  1. #include <cassert>
  2.  
  3. #include <algorithm>
  4. #include <iostream>
  5. #include <iterator>
  6. #include <sstream>
  7. #include <string>
  8.  
  9. class LineIterator: public std::iterator<std::input_iterator_tag,
  10. std::string const>
  11. {
  12. public:
  13. // Default Constructible
  14. LineIterator(): stream(nullptr) {}
  15.  
  16. explicit LineIterator(std::istream& is): stream(&is) { this->advance(); }
  17.  
  18. // Equality Comparable
  19. friend bool operator==(LineIterator const& left, LineIterator const& right) {
  20. return left.stream == right.stream
  21. and left.buffer == right.buffer
  22. and left.currentLine == right.currentLine;
  23. }
  24.  
  25. friend bool operator!=(LineIterator const& left, LineIterator const& right) {
  26. return not (left == right);
  27. }
  28.  
  29. // Trivial Iterator (non mutable)
  30. pointer operator->() const { return &currentLine; }
  31.  
  32. reference operator*() const { return currentLine; }
  33.  
  34. // Input Iterator
  35. LineIterator& operator++() {
  36. this->advance();
  37. return *this;
  38. } // operator++
  39.  
  40. LineIterator operator++(int) {
  41. LineIterator tmp(*this);
  42. ++*this;
  43. return tmp;
  44. } // operator++
  45.  
  46. private:
  47. void advance() {
  48. // Advance a valid iterator to fetch the next line from the source stream.
  49. static LineIterator const SingularValue;
  50.  
  51. assert(*this != SingularValue and "Cannot advance singular iterator");
  52. // Note: in real life, I would use std::getline...
  53. // ... but it would not showcase the double-buffering model
  54. // required to solve the OP problem (because of decoding)
  55.  
  56. // We use double-buffering, so clear current and swap buffers
  57. currentLine.clear();
  58. swap(buffer, currentLine);
  59.  
  60. // Check if we found some new line or not
  61. size_t const nl = currentLine.find('\n');
  62.  
  63. // If we found one already, preserve what's after in the buffer
  64. // as we only want to expose one line worth of material.
  65. if (nl != std::string::npos) {
  66. if (nl == currentLine.size()) { return; } // nothing to preserve
  67.  
  68. buffer.assign(currentLine.begin() + nl + 1, currentLine.end());
  69. currentLine.erase(currentLine.begin() + nl + 1, currentLine.end());
  70. return;
  71. }
  72.  
  73. // If we did not, then we need to pump more data into the buffer.
  74. if (not stream) { return; } // Nothing to pump...
  75.  
  76. static size_t const ReadBufferSize = 256;
  77. char input[ReadBufferSize];
  78.  
  79. while (stream->read(input, ReadBufferSize)) {
  80. if (this->splitBuffer(input, ReadBufferSize)) { break; }
  81. }
  82.  
  83. // We end up here either if we found a new line or if some read failed.
  84. // If the stream is still good, we successfully found a new line!
  85. if (*stream) { return; }
  86.  
  87. // Otherwise, the stream is no good any longer (it dried up!)
  88. // but we may still have read some little things from it.
  89. this->splitBuffer(input, stream->gcount());
  90.  
  91. stream = SingularValue.stream; // stream dried up,
  92. // so reset it to match singular value.
  93. } // advance
  94.  
  95. bool splitBuffer(char const* input, size_t const size) {
  96. // Split input at the newline character, the first chunk ends
  97. // up in currentLine, the second chunk in buffer.
  98. // Returns true if a newline character was found, false otherwise.
  99.  
  100. // Check if we finally found a new line
  101. char const* const newLine = std::find(input, input + size, '\n');
  102.  
  103. // If we did not, copy everything into currentLine and signal it.
  104. if (newLine == input + size) {
  105. currentLine.append(input, size);
  106. return false;
  107. }
  108.  
  109. // If we did, copy everything up to it (including it) into currentLine
  110. // and then bufferize the rest for the next iteration.
  111. currentLine.append(input, newLine + 1);
  112. buffer.assign(newLine + 1, input + size);
  113. return true;
  114. } // splitBuffer
  115.  
  116. std::istream* stream;
  117. std::string buffer;
  118.  
  119. std::string currentLine;
  120. }; // class LineIterator
  121.  
  122.  
  123. int main() {
  124. std::stringstream ss;
  125. ss << "Some string\nwith a couple\n\nnew lines\nthrown in";
  126.  
  127. std::cout << "$";
  128. std::copy(LineIterator(ss),
  129. LineIterator(),
  130. std::ostream_iterator<std::string>(std::cout, "$"));
  131. return 0;
  132. }
Success #stdin #stdout 0s 3436KB
stdin
Standard input is empty
stdout
$Some string
$with a couple
$
$new lines
$thrown in$