open - readfile c++
¿Cómo sangrar fácilmente la salida a ofstream? (7)
Bueno, esta no es la respuesta que estoy buscando, pero en caso de que no haya tal respuesta, aquí hay una forma de hacerlo manualmente:
void
indentedOutput(ostream &outStream, const char *message, bool &newline)
{
while (char cur = *message) {
if (newline) {
outStream << " ";
newline = false;
}
outStream << cur;
if (cur == ''/n'') {
newline = true;
}
++message;
}
}
¿Hay una manera fácil de sangrar la salida que va a un objeto ofstream? Tengo una matriz de caracteres C ++ que es nulo terminar e incluye nuevas líneas. Me gustaría enviar esto a la secuencia, pero sangrar cada línea con dos espacios. ¿Hay una manera fácil de hacer esto con los manipuladores de flujo como puede cambiar la base para salida entera con directivas especiales a la secuencia o tengo que procesar manualmente la matriz e insertar los espacios adicionales manualmente en cada salto de línea detectado?
Parece que el manipulador string :: right () está cerca:
http://www.cplusplus.com/reference/iostream/manipulators/right/
Gracias.
-Guillermo
Esta es la situación perfecta para usar una faceta.
Una versión personalizada de codecvt facet puede imbuirse en una secuencia.
Entonces su uso se vería así:
int main()
{
/* Imbue std::cout before it is used */
std::ios::sync_with_stdio(false);
std::cout.imbue(std::locale(std::locale::classic(), new IndentFacet()));
std::cout << "Line 1/nLine 2/nLine 3/n";
/* You must imbue a file stream before it is opened. */
std::ofstream data;
data.imbue(indentLocale);
data.open("PLOP");
data << "Loki/nUses Locale/nTo do something silly/n";
}
La definición de faceta es ligeramente compleja.
Pero el punto es que alguien que use la faceta no necesita saber nada sobre el formato. El formato se aplica independientemente de cómo se usa la secuencia.
#include <locale>
#include <algorithm>
#include <iostream>
#include <fstream>
class IndentFacet: public std::codecvt<char,char,std::mbstate_t>
{
public:
explicit IndentFacet(size_t ref = 0): std::codecvt<char,char,std::mbstate_t>(ref) {}
typedef std::codecvt_base::result result;
typedef std::codecvt<char,char,std::mbstate_t> parent;
typedef parent::intern_type intern_type;
typedef parent::extern_type extern_type;
typedef parent::state_type state_type;
int& state(state_type& s) const {return *reinterpret_cast<int*>(&s);}
protected:
virtual result do_out(state_type& tabNeeded,
const intern_type* rStart, const intern_type* rEnd, const intern_type*& rNewStart,
extern_type* wStart, extern_type* wEnd, extern_type*& wNewStart) const
{
result res = std::codecvt_base::noconv;
for(;(rStart < rEnd) && (wStart < wEnd);++rStart,++wStart)
{
// 0 indicates that the last character seen was a newline.
// thus we will print a tab before it. Ignore it the next
// character is also a newline
if ((state(tabNeeded) == 0) && (*rStart != ''/n''))
{
res = std::codecvt_base::ok;
state(tabNeeded) = 1;
*wStart = ''/t'';
++wStart;
if (wStart == wEnd)
{
res = std::codecvt_base::partial;
break;
}
}
// Copy the next character.
*wStart = *rStart;
// If the character copied was a ''/n'' mark that state
if (*rStart == ''/n'')
{
state(tabNeeded) = 0;
}
}
if (rStart != rEnd)
{
res = std::codecvt_base::partial;
}
rNewStart = rStart;
wNewStart = wStart;
return res;
}
// Override so the do_out() virtual function is called.
virtual bool do_always_noconv() const throw()
{
return false; // Sometime we add extra tabs
}
};
Ver: las notas de Tom a continuación
He generalizado la solución de Loki Astarti para trabajar con niveles de sangría arbitrarios. La solución tiene una interfaz agradable y fácil de usar, pero la implementación real es un poco sospechosa. Se puede encontrar en github: https://github.com/spacemoose/ostream_indenter
Hay una demo más involucrada en el repositorio github, pero dado:
#include "indent_facet.hpp"
/// This probably has to be called once for every program:
// http://.com/questions/26387054/how-can-i-use-stdimbue-to-set-the-locale-for-stdwcout
std::ios_base::sync_with_stdio(false);
// This is the demo code:
std::cout << "I want to push indentation levels:/n" << indent_manip::push
<< "To arbitrary depths/n" << indent_manip::push
<< "and pop them/n" << indent_manip::pop
<< "back down/n" << indent_manip::pop
<< "like this./n" << indent_manip::pop;
}
Produce el siguiente resultado:
I want to push indentation levels:
To arbitrary depths
and pop them
back down
like this.
Agradecería cualquier comentario sobre la utilidad del código.
He tenido mucho éxito con la sugerencia basada en facetas codecvt de Martin, pero tuve problemas al usarla en std :: cout en OSX, ya que de forma predeterminada esta secuencia usa un streambuf basado en basic_streambuf que ignora la faceta imbuida. La siguiente línea cambia a std :: cout y amigos para que usen un streambuf basado en archivos básicos, que usará la faceta imbuida.
std::ios::sync_with_stdio(false);
Con el efecto secundario asociado de que los objetos de flujo estándar de iostream pueden operar independientemente de los flujos de C estándar.
Otra nota es que esta faceta no tiene una static std :: locale :: id, lo que significa que al llamar a std :: has_facet <IndentFacet> en la configuración regional siempre se devuelve verdadero. Agregar un std :: local :: id significó que la faceta no se usó, ya que basic_filebuf busca la plantilla de la clase base.
Manipulador de espacio en blanco simple
struct Whitespace
{
Whitespace(int n)
: n(n)
{
}
int n;
};
std::ostream& operator<<(std::ostream& stream, const Whitespace &ws)
{
for(int i = 0; i < ws.n; i++)
{
stream << " ";
}
return stream;
}
No hay una manera simple, pero se ha escrito mucho sobre las formas complejas de lograr esto. Lee este artículo para una buena explicación del tema. Aquí hay otro artículo , lamentablemente en alemán. Pero su código fuente debería ayudarte.
Por ejemplo, podría escribir una función que registra una estructura recursiva. Para cada nivel de recursión, la sangría se incrementa:
std::ostream& operator<<(std::ostream& stream, Parameter* rp)
{
stream << "Parameter: " << std::endl;
// Get current indent
int w = format::get_indent(stream);
stream << "Name: " << rp->getName();
// ... log other attributes as well
if ( rp->hasParameters() )
{
stream << "subparameter (" << rp->getNumParameters() << "):/n";
// Change indent for sub-levels in the hierarchy
stream << format::indent(w+4);
// write sub parameters
stream << rp->getParameters();
}
// Now reset indent
stream << format::indent(w);
return stream;
}
Una forma de agregar dicha característica sería escribir un streambuf de filtrado (es decir, un streambuf que reenvía la operación IO a otro streambuf pero manipular los datos transferidos) que agrega la sangría como parte de su operación de filtro. Di un ejemplo de cómo escribir un streambuf aquí y boost proporciona una biblioteca para ayudar en eso.
Si su caso, el miembro overflow () simplemente probaría ''/ n'' y luego agregaría el sangrado justo después, si fuera necesario (exactamente lo que ha hecho en su función indentedOuput
, con la excepción de que la newline
sería miembro del streambuf). Probablemente podría tener una configuración para aumentar o disminuir el tamaño de sangría (tal vez accesible a través de un manipulador, el manipulador tendría que hacer un dynamic_cast para asegurarse de que el streambuf asociado a la secuencia sea del tipo correcto; hay un mecanismo para agregar usuario data to stream - basic_ios :: xalloc, iword y prword - pero aquí queremos actuar en streambuf).