Aquí hay un par de soluciones que funcionarán si está dispuesto a eliminar cualquier espaciado múltiple y/u otro espacio en blanco entre las palabras.
El primer enfoque, que es el más sencillo, sería leer el texto en un istringstream
y extraer palabras de la secuencia. Antes de imprimir cada palabra, verifique si la palabra encajará en la línea actual e imprima una nueva línea si no lo hace. Esta implementación particular no manejará las palabras más largas que la longitud máxima de línea correctamente, pero no sería difícil modificarla para dividir palabras largas.
#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;
}
Una segunda opción más "avanzada", sería escribir una costumbre ostream_iterator
que da formato a las líneas como se espera que sean formateados. Lo llamé ff_ostream_iterator
, por "formateo divertido", pero podría nombrarlo algo más apropiado si quisiera usarlo. Esta implementación divide correctamente palabras largas.
Si bien la aplicación iterador es un poco compleja, el uso es bastante sencillo:
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));
}
La implementación real del iterador es el siguiente:
#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_;
};
[Si copia y pega este fragmento de código y el main
desde arriba, debe compilar y ejecutar si su compilador es compatible con C++ 0x std::shared_ptr
; puede reemplazar eso con boost::shared_ptr
o std::tr1::shared_ptr
si su compilador no tiene compatibilidad con C++ 0x todavía.]
Este enfoque es un poco complicado porque los iteradores deben ser copiables y tenemos que estar seguros de que el texto almacenado en el búfer restante solo se imprime exactamente una vez. Hacemos esto confiando en el hecho de que cada vez que se escribe un iterador de salida, ya no se puede usar ninguna copia de este.
¿Qué has intentado hasta ahora? ¿Has escrito el código que lee el texto dado? –
He intentado usar std :: setw(). He escrito el código que lee el texto dado. Estoy tratando de ver si hay una manera simple de hacer que cout acose automáticamente cada línea (incluidas las líneas producidas a partir de un envoltorio de línea) con un número dado de caracteres. –
Posiblemente no debería haber usado la etiqueta de tarea: estoy trabajando en una tarea, pero la tarea no está relacionada con el formato utilizado para la salida. Tenía curiosidad de cómo se podría lograr este formato. –