単語間の複数のスペースやその他の空白を破棄しても構わないと思っている場合に機能する解決策をいくつか示します。
最も単純な最初のアプローチは、テキストを に読み込み、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;
}
2 番目のより「高度な」オプションは、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 をサポートしていない場合は、これを置き換えることができます。]
イテレータはコピー可能である必要があり、残りのバッファリングされたテキストが正確に 1 回だけ出力されるようにする必要があるため、このアプローチは少し注意が必要です。これは、出力反復子が書き込まれるたびに、そのコピーが使用できなくなるという事実に依存することによって行われます。