Eri*_*c G 7 c++ cout string-formatting
给定一个未知长度的字符串,如何使用cout输出它,以便整个字符串在控制台上显示为缩进的文本块?(这样即使字符串换行到新行,第二行也会有相同的缩进级别)
例:
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;
Run Code Online (Sandbox Code Playgroud)
和期望的输出:
这是一个没有缩进的短字符串.
Run Code Online (Sandbox Code Playgroud)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...
编辑:我正在完成的家庭作业已完成.该赋值与将输出格式化为无关,如上例所示,因此我可能不应该包含homework标记.这只是为了我自己的启发.
我知道我可以计算字符串中的字符,看到当我到达一行的末尾,然后每次吐出换行符并输出-x-空格数.我很想知道是否有更简单,惯用的C++方法来实现上述目标.
如果您愿意丢弃单词之间的任何多个间距和/或其他空格,则可以使用以下几种解决方案.
第一种方法是最简单的方法,即将文本读入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;
}
Run Code Online (Sandbox Code Playgroud)
第二个更"高级"的选项是编写一个自定义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));
}
Run Code Online (Sandbox Code Playgroud)
迭代器的实际实现如下:
#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_;
};
Run Code Online (Sandbox Code Playgroud)
[如果您复制并粘贴此代码片段及其main
上方,如果您的编译器支持C++ 0x,它应该编译并运行std::shared_ptr
; 你可以替换使用boost::shared_ptr
或者std::tr1::shared_ptr
如果你的编译器不具备的C++ 0x的支持呢.]
这种方法有点棘手,因为迭代器必须是可复制的,我们必须确保任何剩余的缓冲文本只打印一次.我们依靠以下事实来做到这一点:无论何时写入输出迭代器,它的任何副本都不再可用.
这仍然需要一些工作(例如,indent
应该可以作为操纵器来实现,但是带有参数的操纵器很难可移植地编写——标准并不真正支持/定义它们)。可能至少有几个不完美的极端情况(例如,现在,它将退格视为正常字符)。
#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.";
}
Run Code Online (Sandbox Code Playgroud)
请注意,这indent(0)
是一种特殊情况。通常缩进从 0 开始。调用yourstream.indent(number)
wherenumber
是正数或负数会相对于前一个值调整缩进。yourstream.indent(0)
不会做任何事情,但我已经将它设置为特殊情况以将缩进重置为 0(绝对)。如果这得到认真使用,我不确定从长远来看这会是最好的,但至少对于演示来说它似乎足够了。
归档时间: |
|
查看次数: |
10913 次 |
最近记录: |