[ACCEPTED]-Indenting Paragraph With cout-cout
Here are a couple of solutions that will 33 work if you are willing to throw out any 32 multiple spacing and/or other whitespace 31 between words.
The first approach, which 30 is the most straightforward, would be to 29 read the text into an istringstream
and extract words 28 from the stream. Before printing each word, check 27 to see whether the word will fit on the 26 current line and print a newline if it won't. This 25 particular implementation won't handle words 24 longer than the maximum line length correctly, but 23 it wouldn't be difficult to modify it to 22 split 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;
}
A second, more "advanced" option, would 21 be to write a custom ostream_iterator
that formats lines 20 as you expect them to be formatted. I've 19 named this ff_ostream_iterator
, for "funny formatting," but 18 you could name it something more appropriate 17 if you wanted to use it. This implementation 16 does correctly split long words.
While the 15 iterator implementation is a bit complex, the 14 usage is quite straightforward:
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 13 implementation of the iterator 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 12 you copy and paste this code snippet and 11 the main
from above it, it should compile and 10 run if your compiler supports the C++0x 9 std::shared_ptr
; you can replace that with boost::shared_ptr
or std::tr1::shared_ptr
if your 8 compiler doesn't have C++0x support yet.]
This 7 approach is a bit tricky because iterators 6 have to be copyable and we have to be sure 5 that any remaining buffered text is only 4 printed exactly once. We do this by relying 3 on the fact that any time an output iterator 2 is written to, any copies of it are no longer 1 usable.
This could still use a little bit of work 17 (e.g., the indent
should probably be implemented 16 as a manipulator, but manipulators with 15 arguments are hard to write portably -- the 14 standard doesn't really support/define them). There 13 are probably at least a couple of corner 12 cases that aren't perfect (e.g., right now, it 11 treats back-space as if it were a normal 10 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's 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. Normally 9 indentation starts out at 0. calling yourstream.indent(number)
where 8 number
is either positive or negative adjusts 7 the indentation relative to the previous 6 value. yourstream.indent(0)
wouldn't do anything, but I've special-cased 5 it to reset the indentation to 0 (as an 4 absolute). If this gets put to serious use, I'm 3 not sure that'll work out the best in the 2 long term, but at least for the demo it 1 appears sufficient.
I'm not sure this is the way to do it, but 5 if you know or can assume the screen width, my 4 first thought is to remove the first screenWidth - indent
chars 3 from the string and print them with the 2 preceding spaces, and keep doing that until 1 you've done the whole string.
You could always use '\t'
to indent the line 3 instead of a set number of characters, but 2 I know of no simpler way to implement the 1 logic without introducing external libraries.
This problem can be reduced to a task of 24 minimizing amount of wasted space on each 23 line. Suppose we have words of following 22 lengths
{ 6,7,6,8,10,3,4,10 }
If we calculate amount of space that 21 will be wasted when we arrange last n words 20 without breaking them and put it into a 19 table then we can find optimum number of 18 words to print on current line going forward.
Here 17 is an example for 20 character wide screen. In 16 this table first column is number of last 15 words, second column is length of n'th word 14 from the end and third is the minimum space 13 wasted:
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 when we have only one 12 last word of 10 letters 10 letters are wasted, if 11 we have 2 words with second from the end 10 4 characters long we will have 5 letters 9 wasted (one space between words) extra 3 8 letter word will leave only one spaces wasted. Adding 7 another 10 letter word leaves us with 11 6 letters wasted total on 2 lines and so on.
Example
6, 7, 5 (0)
8, 10 (1)
3, 4, 10 (1)
If 5 we choose to print 2 words on first line 4 wasted space is indeed 14. Numbers in () show 3 wasted space.
6, 7 (6)
5, 8 (6)
10, 3, 4 (2)
4, 10 (6)
I think this is a well known 2 problem and algorithm I have described is 1 an example of dynamic programming.
More Related questions
We use cookies to improve the performance of the site. By staying on our site, you agree to the terms of use of cookies.