Here are some solutions that will work if you want to throw between multiple intervals and / or other spaces between words.
The first approach, which is the simplest, would be to read the text in istringstream and extract the words from the stream. Before printing each word, check whether the word matches the current line and print a new line if it does not. This particular implementation will not process words longer than the maximum line length correctly, but it will not be difficult to change it to separate long words.
#include <iostream> #include <sstream> #include <string> int main() { const unsigned max_line_length(40); const std::string line_prefix(" "); const std::string text( "Friends, Romans, countrymen, lend me your ears; I come to bury Caesar," " not to praise him. The evil that men do lives after them; The good " "is oft interred with their bones; So let it be with Caesar."); std::istringstream text_iss(text); std::string word; unsigned characters_written = 0; std::cout << line_prefix; while (text_iss >> word) { if (word.size() + characters_written > max_line_length) { std::cout << "\n" << line_prefix; characters_written = 0; } std::cout << word << " "; characters_written += word.size() + 1; } std::cout << std::endl; }
The second, more โadvancedโ option would be to write a custom ostream_iterator that formats the strings, as you expect, they will be formatted. I called it ff_ostream_iterator for โfunny formatting,โ but you could call it more appropriate if you want to use it. This implementation correctly separates long words.
Although implementing an iterator is a bit complicated, using it is pretty simple:
int main() { const std::string text( "Friends, Romans, countrymen, lend me your ears; I come to bury Caesar," " not to praise him. The evil that men do lives after them; The good " "is oft interred with their bones; So let it be with Caesar. ReallyLong" "WordThatWontFitOnOneLineBecauseItIsSoFreakinLongSeriouslyHowLongIsThis" "Word"); std::cout << " ========================================" << std::endl; std::copy(text.begin(), text.end(), ff_ostream_iterator(std::cerr, " ", 40)); }
The actual iterator implementation is as follows:
#include <cctype> #include <iostream> #include <iterator> #include <memory> #include <sstream> #include <string> class ff_ostream_iterator : public std::iterator<std::output_iterator_tag, char, void, void, void> { public: ff_ostream_iterator() { } ff_ostream_iterator(std::ostream& os, std::string line_prefix, unsigned max_line_length) : os_(&os), line_prefix_(line_prefix), max_line_length_(max_line_length), current_line_length_(), active_instance_(new ff_ostream_iterator*(this)) { *os_ << line_prefix; } ~ff_ostream_iterator() { if (*active_instance_ == this) insert_word(); } ff_ostream_iterator& operator=(char c) { *active_instance_ = this; if (std::isspace(c)) { if (word_buffer_.size() > 0) { insert_word(); } } else { word_buffer_.push_back(c); } return *this; } ff_ostream_iterator& operator*() { return *this; } ff_ostream_iterator& operator++() { return *this; } ff_ostream_iterator operator++(int) { return *this; } private: void insert_word() { if (word_buffer_.size() == 0) return; if (word_buffer_.size() + current_line_length_ <= max_line_length_) { write_word(word_buffer_); } else { *os_ << '\n' << line_prefix_; if (word_buffer_.size() <= max_line_length_) { current_line_length_ = 0; write_word(word_buffer_); } else { for (unsigned i(0);i<word_buffer_.size();i+=max_line_length_) { current_line_length_ = 0; write_word(word_buffer_.substr(i, max_line_length_)); if (current_line_length_ == max_line_length_) { *os_ << '\n' << line_prefix_; } } } } word_buffer_ = ""; } void write_word(const std::string& word) { *os_ << word; current_line_length_ += word.size(); if (current_line_length_ != max_line_length_) { *os_ << ' '; ++current_line_length_; } } std::ostream* os_; std::string word_buffer_; std::string line_prefix_; unsigned max_line_length_; unsigned current_line_length_; std::shared_ptr<ff_ostream_iterator*> active_instance_; };
[If you copy and paste this piece of code and main above, it should compile and run if your compiler supports C ++ 0x std::shared_ptr ; you can replace it with boost::shared_ptr or std::tr1::shared_ptr if your compiler does not yet support C ++ 0x.]
This approach is a bit more complicated, because iterators must be copyable, and we must be sure that any remaining buffer text is printed exactly once. We do this by relying on the fact that at any time when the output iterator is written, any copies of it can no longer be used.