Вот несколько решений, которые будут работать, если вы готовы отказаться от любых множественных пробелов и/или других пробелов между словами.
Первый подход, который является наиболее простым, заключается в чтении текста в 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.]
Этот подход немного сложен, потому что итераторы должны быть копируемыми, и мы должны быть уверены, что любой оставшийся буферизованный текст будет напечатан только один раз. Мы делаем это, полагаясь на тот факт, что каждый раз, когда производится запись в выходной итератор, любые его копии больше нельзя использовать.
person
James McNellis
schedule
12.03.2011