online indenter indentar indentador indent identar corrector codigo code beauty c++ pretty-print

indenter - indentar codigo c++



ImpresiĆ³n de listas con comas C++ (24)

Éste sobrecarga el operador de flujo. Sí, las variables globales son malvadas.

#include <iostream> #include <string> #include <vector> #include <algorithm> #include <iterator> int index = 0; template<typename T, template <typename, typename> class Cont> std::ostream& operator<<(std::ostream& os, const Cont<T, std::allocator<T>>& vec) { if (index < vec.size()) { if (index + 1 < vec.size()) return os << vec[index++] << "-" << vec; else return os << vec[index++] << vec; } else return os; } int main() { std::vector<int> nums(10); int n{0}; std::generate(nums.begin(), nums.end(), [&]{ return n++; }); std::cout << nums << std::endl; }

Sé cómo hacer esto en otros idiomas, pero no en C ++, que estoy obligado a usar aquí.

Tengo un conjunto de cadenas para imprimir en una lista, y necesitan una coma entre cada una, pero no una coma al final. En Java, por ejemplo, usaría un generador de cuerdas y simplemente eliminaría la coma del final después de haber construido mi cadena. ¿Cómo lo hago en C ++?

auto iter = keywords.begin(); for (iter; iter != keywords.end( ); iter++ ) { out << *iter << ", "; } out << endl;

Inicialmente traté de insertar este bloque para hacerlo (moviendo la impresión de coma aquí)

if (iter++ != keywords.end()) out << ", "; iter--;

Odio cuando las cosas pequeñas me hacen tropezar.

EDITAR: Gracias a todos. Es por eso que publico cosas como esta aquí. Tantas buenas respuestas, y abordadas de diferentes maneras. Después de un semestre de Java y ensamblado (diferentes clases), tener que hacer un proyecto de C ++ en 4 días me lanzó a un bucle. No solo obtuve mi respuesta, tuve la oportunidad de pensar en las diferentes formas de abordar un problema como este. Increíble.


¿Algo como esto?

while (iter != keywords.end()) { out << *iter; iter++; if (iter != keywords.end()) cout << ", "; }


Asumiendo un flujo de salida vagamente normal, de modo que escribir una cadena vacía no hace nada:

const char *padding = ""; for (auto iter = keywords.begin(); iter != keywords.end(); ++iter) { out << padding << *iter; padding = ", " }


Creo que esto debería funcionar

while (iter != keywords.end( )) { out << *iter; iter++ ; if (iter != keywords.end( )) out << ", "; }


Debido a que todos han decidido hacer esto con while bucles, daré un ejemplo con bucles for.

for (iter = keywords.begin(); iter != keywords.end(); iter++) { if (iter != keywords.begin()) cout << ", "; cout << *iter; }


En Python, solo escribimos:

print ", ".join(keywords)

entonces por qué no:

template<class S, class V> std::string join(const S& sep, const V& v) { std::ostringstream oss; if (!v.empty()) { typename V::const_iterator it = v.begin(); oss << *it++; for (typename V::const_iterator e = v.end(); it != e; ++it) oss << sep << *it; } return oss.str(); }

y luego solo úsalo como:

cout << join(", ", keywords) << endl;

A diferencia del ejemplo de python anterior donde " " es una cadena y las keywords tienen que ser un iterable de cadenas, aquí en este ejemplo de C ++, el separador y las keywords pueden ser cualquier cosa que se pueda transmitir, por ejemplo

cout << join(''/n'', keywords) << endl;


En un compilador experimental listo para C ++ 17 que le llegará próximamente, puede usar std::experimental::ostream_joiner :

#include <algorithm> #include <experimental/iterator> #include <iostream> #include <iterator> int main() { int i[] = {1, 2, 3, 4, 5}; std::copy(std::begin(i), std::end(i), std::experimental::make_ostream_joiner(std::cout, ", ")); }

Ejemplos en vivo usando GCC 6.0 SVN y Clang 3.9 SVN


Hay muchas soluciones inteligentes y demasiadas que destruyen el código más allá de la esperanza de salvación sin permitir que el compilador haga su trabajo.

La solución obvia es, en caso especial, la primera iteración:

bool first = true; for (auto const& e: sequence) { if (first) { first = false; } else { out << ", "; } out << e; }

Es un patrón simple muerto que:

  1. No destruye el ciclo: todavía es obvio a simple vista que cada elemento se repetirá.
  2. Permite algo más que poner un separador o, de hecho, imprimir una lista, ya que el bloque else y el cuerpo del bucle pueden contener declaraciones arbitrarias.

Puede que no sea el código más eficiente, pero la posible pérdida de rendimiento de una sola rama bien pronosticada será eclipsada por el gigante masivo que es std::ostream::operator<< .


Hay un pequeño problema con el operador ++ que está utilizando.

Puedes probar:

if (++iter != keywords.end()) out << ", "; iter--;

De esta forma, ++ se evaluará antes de comparar el iterador con keywords.end() .


Lo siguiente debería hacer:

const std::vector<__int64>& a_setRequestId std::stringstream strStream; std::copy(a_setRequestId.begin(), a_setRequestId.end() -1, std::ostream_iterator<__int64>(strStream, ", ")); strStream << a_setRequestId.back();


Me gustaría ir con algo como esto, una solución fácil y debería funcionar para todos los iteradores.

int maxele = maxele = v.size() - 1; for ( cur = v.begin() , i = 0; i < maxele ; ++i) { std::cout << *cur++ << " , "; } if ( maxele >= 0 ) { std::cout << *cur << std::endl; }


Mi método típico para hacer separadores (en cualquier idioma ) es usar un ciclo de prueba media. El código de C ++ sería:

for (;;) { std::cout << *iter; if (++iter == keywords.end()) break; std::cout << ","; }

(nota: un cheque adicional if es necesario antes del ciclo si las palabras clave pueden estar vacías)

La mayoría de las otras soluciones que se muestran terminan realizando una prueba adicional completa en cada iteración de bucle. Estás haciendo I / O, por lo que el tiempo empleado no es un gran problema, pero ofende mi sensibilidad.


Otra posible solución, que evita un if

Char comma = ''[''; for (const auto& element : elements) { std::cout.put(comma) << element; comma = '',''; } std::cout.put('']'');

Depende de lo que estás haciendo en tu ciclo.


Podría ser así ...

bool bFirst = true; for (auto curr = keywords.begin(); curr != keywords.end(); ++curr) { std::cout << (bFirst ? "" : ", ") << *curr; bFirst = false; }


Prueba esto:

typedef std::vector<std::string> Container; typedef Container::const_iterator CIter; Container data; // Now fill the container. // Now print the container. // The advantage of this technique is that ther is no extra test during the loop. // There is only one additional test !test.empty() done at the beginning. if (!data.empty()) { std::cout << data[0]; for(CIter loop = data.begin() + 1; loop != data.end(); ++loop) { std::cout << "," << *loop; } }


Puede usar funtores:

#include <functional> string getSeparatedValues(function<bool()> condition, function<string()> output, string separator) { string out; out += output(); while (condition()) out += separator + output(); return out; }

Ejemplo:

if (!keywords.empty()) { auto iter = keywords.begin(); cout << getSeparatedValues([&]() { return ++iter != keywords.end(); }, [&]() { return *iter; }, ", ") << endl; }


Puede usar un bucle do , reescribir la condición de bucle para la primera iteración y usar el operador de cortocircuito && y el hecho de que una transmisión válida es true .

auto iter = keywords.begin(); if ( ! keywords.empty() ) do { out << * iter; } while ( ++ iter != keywords.end() && out << ", " ); out << endl;


Si los valores son std::string s, puede escribir esto muy bien en un estilo declarativo con range-v3

#include <range/v3/all.hpp> #include <vector> #include <iostream> #include <string> int main() { using namespace ranges; std::vector<std::string> const vv = { "a","b","c" }; auto joined = vv | view::join('',''); std::cout << to_<std::string>(joined) << std::endl; }

Para otros tipos que deben convertirse en cadenas, puede agregar una transformación que llame a to_string .

#include <range/v3/all.hpp> #include <vector> #include <iostream> #include <string> int main() { using namespace ranges; std::vector<int> const vv = { 1,2,3 }; auto joined = vv | view::transform([](int x) {return std::to_string(x);}) | view::join('',''); std::cout << to_<std::string>(joined) << std::endl; }


Te sugiero que simplemente cambies el primer personaje con la ayuda de un lambda.

std::function<std::string()> f = [&]() {f = [](){ return ","; }; return ""; }; for (auto &k : keywords) std::cout << f() << k;


Un enfoque común es imprimir el primer elemento antes del bucle, y hacer un bucle solo sobre los elementos restantes, PRE-imprimiendo una coma antes de cada artículo restante.

Alternativamente, debería poder crear su propia transmisión que mantenga un estado actual de la línea (antes de endl) y poner comas en el lugar apropiado.

EDITAR: También puede usar un ciclo de prueba intermedia como lo sugiere TED. Sería algo así como:

if(!keywords.empty()) { auto iter = keywords.begin(); while(true) { out << *iter; ++iter; if(iter == keywords.end()) { break; } else { out << ", "; } } }

Primero mencioné el método "imprimir primero elemento antes del ciclo" porque mantiene el cuerpo del ciclo realmente simple, pero cualquiera de los enfoques funciona bien.


Usando impulso:

std::string add_str(""); const std::string sep(","); for_each(v.begin(), v.end(), add_str += boost::lambda::ret<std::string>(boost::lambda::_1 + sep));

y obtienes una cadena que contiene el vector, delimitada por comas.

EDITAR: para eliminar la última coma, simplemente emita:

add_str = add_str.substr(0, add_str.size()-1);


Use un infix_iterator:

// infix_iterator.h // // Lifted from Jerry Coffin''s ''s prefix_ostream_iterator #if !defined(INFIX_ITERATOR_H_) #define INFIX_ITERATOR_H_ #include <ostream> #include <iterator> template <class T, class charT=char, class traits=std::char_traits<charT> > class infix_ostream_iterator : public std::iterator<std::output_iterator_tag,void,void,void,void> { std::basic_ostream<charT,traits> *os; charT const* delimiter; bool first_elem; public: typedef charT char_type; typedef traits traits_type; typedef std::basic_ostream<charT,traits> ostream_type; infix_ostream_iterator(ostream_type& s) : os(&s),delimiter(0), first_elem(true) {} infix_ostream_iterator(ostream_type& s, charT const *d) : os(&s),delimiter(d), first_elem(true) {} infix_ostream_iterator<T,charT,traits>& operator=(T const &item) { // Here''s the only real change from ostream_iterator: // Normally, the ''*os << item;'' would come before the ''if''. if (!first_elem && delimiter != 0) *os << delimiter; *os << item; first_elem = false; return *this; } infix_ostream_iterator<T,charT,traits> &operator*() { return *this; } infix_ostream_iterator<T,charT,traits> &operator++() { return *this; } infix_ostream_iterator<T,charT,traits> &operator++(int) { return *this; } }; #endif

El uso sería algo así como:

#include "infix_iterator.h" // ... std::copy(keywords.begin(), keywords.end(), infix_iterator(out, ","));


Yo uso una pequeña clase de ayudante para eso:

class text_separator { public: text_separator(const char* sep) : sep(sep), needsep(false) {} // returns an empty string the first time it is called // returns the provided separator string every other time const char* operator()() { if (needsep) return sep; needsep = true; return ""; } void reset() { needsep = false; } private: const char* sep; bool needsep; };

Para usarlo:

text_separator sep(", "); for (int i = 0; i < 10; ++i) cout << sep() << i;


para evitar colocar un if dentro del ciclo, uso esto:

vector<int> keywords = {1, 2, 3, 4, 5}; if (!keywords.empty()) { copy(keywords.begin(), std::prev(keywords.end()), std::ostream_iterator<int> (std::cout,", ")); std::cout << keywords.back(); }

Depende del tipo de vector, int , pero puede eliminarlo con algún ayudante.