Indent paragraph with cout

Given a string of unknown length, how can you output it with cout so that the entire string appears as indents in the text on the console? (so even if the line wraps in a new line, the second line will have the same level of indentation)

Example:

cout << "This is a short string that isn't indented." << endl; cout << /* Indenting Magic */ << "This is a very long string that will wrap to the next line because it is a very long string that will wrap to the next line..." << endl; 

And the desired conclusion:

This is a short line that is indented.

  This is a very long string that will wrap to the next line because it is a very long string that will wrap to the next line... 

Edit: The homework I'm working on is complete. The assignment has nothing to do with getting the output format, as in the example above, so I probably shouldn't have included the homework tag. It is only for my own enlightenment.

I know that I can count characters in a line, see when I get to the end of the line, and then spit out a new line and output -x- the number of spaces each time. I am interested to know if there is a simpler, idiomatic C ++ way to accomplish the above.

+6
c ++ string-formatting cout
source share
5 answers

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.

+7
source share

This may still use a bit of work (for example, indent really needs to be implemented as a manipulator, but argument manipulators are hard to write portable - the standard doesn't actually support / define them). I believe that he still needs to work a bit on how he processes newlines, and [Edit: I think I fixed the problems that I saw there.] There are probably at least a couple of corners of cases that are not perfect [Edit : for example, right now, it is processing backspace as if it were a regular character].

 #include <iostream> #include <streambuf> #include <iomanip> class widthbuf: public std::streambuf { public: widthbuf(int w, std::streambuf* s): indent_width(0), def_width(w), width(w), sbuf(s), count(0) {} ~widthbuf() { overflow('\n'); } void set_indent(int w) { if (w == 0) { prefix.clear(); indent_width = 0; width = def_width; } else { indent_width += w; prefix = std::string(indent_width, ' '); width -= w; } } private: typedef std::basic_string<char_type> string; // This is basically a line-buffering stream buffer. // The algorithm is: // - Explicit end of line ("\r" or "\n"): we flush our buffer // to the underlying stream buffer, and set our record of // the line length to 0. // - An "alert" character: sent to the underlying stream // without recording its length, since it doesn't normally // affect the a appearance of the output. // - tab: treated as moving to the next tab stop, which is // assumed as happening every tab_width characters. // - Everything else: really basic buffering with word wrapping. // We try to add the character to the buffer, and if it exceeds // our line width, we search for the last space/tab in the // buffer and break the line there. If there is no space/tab, // we break the line at the limit. int_type overflow(int_type c) { if (traits_type::eq_int_type(traits_type::eof(), c)) return traits_type::not_eof(c); switch (c) { case '\n': case '\r': { buffer += c; count = 0; sbuf->sputn(prefix.c_str(), indent_width); int_type rc = sbuf->sputn(buffer.c_str(), buffer.size()); buffer.clear(); return rc; } case '\a': return sbuf->sputc(c); case '\t': buffer += c; count += tab_width - count % tab_width; return c; default: if (count >= width) { size_t wpos = buffer.find_last_of(" \t"); if (wpos != string::npos) { sbuf->sputn(prefix.c_str(), indent_width); sbuf->sputn(buffer.c_str(), wpos); count = buffer.size()-wpos-1; buffer = string(buffer, wpos+1); } else { sbuf->sputn(prefix.c_str(), indent_width); sbuf->sputn(buffer.c_str(), buffer.size()); buffer.clear(); count = 0; } sbuf->sputc('\n'); } buffer += c; ++count; return c; } } size_t indent_width; size_t width, def_width; size_t count; size_t tab_count; static const int tab_width = 8; std::string prefix; std::streambuf* sbuf; string buffer; }; class widthstream : public std::ostream { widthbuf buf; public: widthstream(size_t width, std::ostream &os) : buf(width, os.rdbuf()), std::ostream(&buf) {} widthstream &indent(int w) { buf.set_indent(w); return *this; } }; int main() { widthstream out(30, std::cout); out.indent(10) << "This is a very long string that will wrap to the next line because it is a very long string that will wrap to the next line.\n"; out.indent(0) << "This is\tsome\tmore text that should not be indented but should still be word wrapped to 30 columns."; } 

Note that indent(0) is a special case. yourstream.indent(number) usually starts at 0. call yourstream.indent(number) , where number is either positive or negative, adjusts the indentation relative to the previous value. yourstream.indent(0) not do anything, but I highlighted it with reset indent to 0 (as absolute). If this seriously benefits, Iโ€™m not sure that in the long run it will be better, but at least for demonstration it looks pretty convenient.

+4
source share

I'm not sure if this is the way to do this, but if you know or can accept the width of the screen, my first thought is to remove the first lines of screenWidth - indent from the line and print them with the previous spaces, and continue to do this until you complete the entire line.

+1
source share

You can always use '\t' to indent a line instead of a given number of characters, but I donโ€™t know an easier way to implement logic without introducing external libraries.

+1
source share

This problem can be reduced to the problem of minimizing the amount of lost space on each row. Suppose we have words of the following lengths

 { 6,7,6,8,10,3,4,10 } 

If we calculate the amount of space that will be wasted when we arrange the last n words without breaking them and putting them in the table, we can find the optimal number of words to print on the current line in the future.

Here is an example of a screen with a width of 20 characters. In this table, the first column is the number of last words, the second column is the length of the nth word from the end, and the third is the minimum space for nothing:

 8 6 1 7 7 7 6 5 14 5 8 2 4 10 11 3 3 1 2 4 5 1 10 10 

For example, if we have only one last word of 10 letters, then 10 letters will be wasted, if we have 2 words with a second from the end of 4 characters, we will have 5 letters in wasted (one place between words), additional 3- a literal word will leave only one space for nothing. Adding 10 more letter words leaves us with 11 letters wasted for 2 lines, etc.

Example

 6, 7, 5 (0) 8, 10 (1) 3, 4, 10 (1) 

If we choose to print 2 words on the first line, the waste space is really 14. The numbers in () show the empty space.

 6, 7 (6) 5, 8 (6) 10, 3, 4 (2) 4, 10 (6) 

I think this is a well-known problem, and the algorithm I described is an example of dynamic programming.

0
source share

All Articles