Вот пара решений, которые сработают, если вы захотите выбросить несколько пробелов и / или другие пробелы между словами.
Первый подход, который является наиболее простым, состоит в том, чтобы прочитать текст в istringstream
и извлечь слова из потока. Перед печатью каждого слова проверьте, поместится ли оно в текущей строке, и напечатайте новую строку, если этого не произойдет. Эта конкретная реализация не будет правильно обрабатывать слова, длина которых превышает максимальную длину строки, но не составит труда изменить ее для разделения длинных слов.
#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;
}
Второй, более «продвинутый» вариант - написать пользовательский ostream_iterator
, который форматирует строки так, как вы ожидаете, что они будут отформатированы. Я назвал это ff_ostream_iterator
для «забавного форматирования», но вы могли бы назвать его более подходящим, если хотите его использовать. Эта реализация правильно разбивает длинные слова.
Хотя реализация итератора немного сложна, использование довольно простое:
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));
}
Фактическая реализация итератора выглядит следующим образом:
#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_;
};
[Если вы скопируете и вставите этот фрагмент кода и main
сверху, он должен скомпилироваться и запустить, если ваш компилятор поддерживает C ++ 0x std::shared_ptr
; вы можете заменить это на boost::shared_ptr
или std::tr1::shared_ptr
, если ваш компилятор еще не поддерживает C ++ 0x.]
Этот подход немного сложен, потому что итераторы должны быть копируемыми, и мы должны быть уверены, что любой оставшийся буферизованный текст печатается только один раз. Мы делаем это, полагаясь на тот факт, что каждый раз, когда в выходной итератор записываются, любые его копии больше не могут использоваться.