c++ - una - ¿Cómo imprimir el contenido de un vector?
imprimir un vector en c (13)
Quiero imprimir el contenido de un vector en C ++, esto es lo que tengo:
#include <iostream>
#include <fstream>
#include <string>
#include <cmath>
#include <vector>
#include <sstream>
#include <cstdio>
using namespace std;
int main()
{
ifstream file("maze.txt");
if (file) {
vector<char> vec(istreambuf_iterator<char>(file), (istreambuf_iterator<char>()));
vector<char> path;
int x = 17;
char entrance = vec.at(16);
char firstsquare = vec.at(x);
if (entrance == ''S'') {
path.push_back(entrance);
}
for (x = 17; isalpha(firstsquare); x++) {
path.push_back(firstsquare);
}
for (int i = 0; i < path.size(); i++) {
cout << path[i] << " ";
}
cout << endl;
return 0;
}
}
¿Cómo imprimo el contenido del vector en la pantalla?
Usando std::copy
pero sin separador extra
Un enfoque alternativo / modificado que utiliza std::copy
(como se usó originalmente en la respuesta de @JoshuaKravtiz ) pero sin incluir un separador de arrastre adicional después del último elemento:
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
template <typename T>
void print_contents(const std::vector<T>& v, const char * const separator = " ")
{
if(!v.empty())
{
std::copy(v.begin(),
--v.end(),
std::ostream_iterator<T>(std::cout, separator));
std::cout << v.back() << "/n";
}
}
// example usage
int main() {
std::vector<int> v{1, 2, 3, 4};
print_contents(v); // ''1 2 3 4''
print_contents(v, ":"); // ''1:2:3:4''
v = {};
print_contents(v); // ... no std::cout
v = {1};
print_contents(v); // ''1''
return 0;
}
Ejemplo de uso aplicado al contenedor de un tipo de POD personalizado:
// includes and ''print_contents(...)'' as above ...
class Foo
{
int i;
friend std::ostream& operator<<(std::ostream& out, const Foo& obj);
public:
Foo(const int i) : i(i) {}
};
std::ostream& operator<<(std::ostream& out, const Foo& obj)
{
return out << "foo_" << obj.i;
}
int main() {
std::vector<Foo> v{1, 2, 3, 4};
print_contents(v); // ''foo_1 foo_2 foo_3 foo_4''
print_contents(v, ":"); // ''foo_1:foo_2:foo_3:foo_4''
v = {};
print_contents(v); // ... no std::cout
v = {1};
print_contents(v); // ''foo_1''
return 0;
}
¿Qué hay de for_each
+ lambda expresión :
#include <vector>
#include <algorithm>
...
std::vector<char> vec;
...
std::for_each(
vec.cbegin(),
vec.cend(),
[] (const char c) {std::cout << c << " ";}
);
...
Por supuesto, un rango basado en es la solución más elegante para esta tarea concreta, pero esta también ofrece muchas otras posibilidades.
Explicación
El algoritmo for_each
toma un rango de entrada y un objeto invocable , llamando a este objeto en cada elemento del rango. Un intervalo de entrada está definido por dos iteradores . Un objeto llamable puede ser una función, un puntero a la función, un objeto de una clase que sobrecarga al () operator
o, como en este caso, una expresión lambda . El parámetro para esta expresión coincide con el tipo de los elementos del vector.
La belleza de esta implementación es el poder que obtiene de las expresiones lambda: puede utilizar este enfoque para muchas más cosas que solo imprimir el vector.
Creo que la mejor manera de hacerlo es simplemente sobrecargar el operator<<
agregando esta función a su programa:
#include <vector>
using std::vector;
#include <iostream>
using std::ostream;
template<typename T>
ostream& operator<< (ostream& out, const vector<T>& v) {
out << "{";
size_t last = v.size() - 1;
for(size_t i = 0; i < v.size(); ++i) {
out << v[i];
if (i != last)
out << ", ";
}
out << "}";
return out;
}
Luego, puede usar el operador <<
en cualquier vector posible, asumiendo que sus elementos también tienen ostream& operator<<
definido:
vector<string> s = {"first", "second", "third"};
vector<bool> b = {true, false, true, false, false};
vector<int> i = {1, 2, 3, 4};
cout << s << endl;
cout << b << endl;
cout << i << endl;
Salidas:
{first, second, third}
{1, 0, 1, 0, 0}
{1, 2, 3, 4}
El problema probablemente se encuentre en el bucle anterior: (x = 17; isalpha(firstsquare); x++)
. Este bucle no se ejecutará en absoluto (si firstsquare
no es alfa) o se ejecutará para siempre (si es alfa). La razón es que firstsquare
no cambia a medida que x
se incrementa.
En C ++ 11 ahora puedes usar un bucle para rango basado en :
for (auto const& c : path)
std::cout << c << '' '';
En C ++ 11, un bucle for basado en rango podría ser una buena solución:
vector<char> items = {''a'',''b'',''c''};
for (char n : items)
cout << n << '' '';
Salida:
a b c
En C ++ 11``
for (auto i = path.begin(); i != path.end(); ++i)
std::cout << *i << '' '';
for(int i=0; i<path.size(); ++i)
std::cout << path[i] << '' '';
Esta respuesta se basa en la respuesta de Zorawar, pero no podía dejar un comentario allí.
Puede hacer que la versión auto (C ++ 11) / typedef const sea usando cbegin y cend en su lugar
for (auto i = path.cbegin(); i != path.cend(); ++i)
std::cout << *i << '' '';
Sólo tienes que copiar el contenedor a la consola.
std::vector<int> v{1,2,3,4};
std::copy(v.begin(),v.end(),std::ostream_iterator<int>(std::cout, " " ));
Debe salir:
1 2 3 4
Simplemente para responder a su pregunta, puede usar un iterador:
std::vector<char> path;
// ...
for (std::vector<char>::const_iterator i = path.begin(); i != path.end(); ++i)
std::cout << *i << '' '';
Si desea modificar el contenido del vector en el bucle for, utilice iterator
lugar de const_iterator
.
Pero hay mucho más que se puede decir sobre esto. Si solo quiere una respuesta que pueda usar, entonces puede detenerse aquí; de lo contrario, sigue leyendo.
auto (C ++ 11) / typedef
Esta no es otra solución, sino un complemento de la solución del iterator
anterior. Si está utilizando el estándar C ++ 11 (o posterior), puede usar la palabra clave auto
para facilitar la legibilidad:
for (auto i = path.begin(); i != path.end(); ++i)
std::cout << *i << '' '';
Pero el tipo de i
será no constante (es decir, el compilador usará std::vector<char>::iterator
como el tipo de i
).
En este caso, también puede utilizar un typedef
(no restringido a C ++ 11, y muy útil para usar de todos modos):
typedef std::vector<char> Path;
Path path;
// ...
for (Path::const_iterator i = path.begin(); i != path.end(); ++i)
std::cout << *i << '' '';
mostrador
Por supuesto, puede usar un tipo entero para registrar su posición en el bucle for
:
for(int i=0; i<path.size(); ++i)
std::cout << path[i] << '' '';
Si va a hacer esto, es mejor usar los tipos de miembros del contenedor, si están disponibles y son apropiados. std::vector
tiene un tipo de miembro llamado size_type
para este trabajo: es el tipo devuelto por el método size
.
// Path typedef''d to std::vector<char>
for( Path::size_type i=0; i<path.size(); ++i)
std::cout << path[i] << '' '';
¿Por qué no usar esto sobre la solución de iterator
? Para casos simples, también podría hacerlo, pero el punto es que la clase de iterator
es un objeto diseñado para hacer este trabajo para objetos más complicados donde esta solución no va a ser ideal.
rango basado en bucle (C ++ 11)
Ver la solución de Jefffrey . En C ++ 11 (y versiones posteriores) puede usar el nuevo bucle for
rango, que se ve así:
for (auto i: path)
std::cout << i << '' '';
Dado que path
es un vector de elementos (explícitamente std::vector<char>
), el objeto i
es del tipo del elemento del vector (es decir, explícitamente, es de tipo char
). El objeto i
tiene un valor que es una copia del elemento real en el objeto de path
. Por lo tanto, todos los cambios en i
en el bucle no se conservan en la path
en sí. Además, si desea imponer el hecho de que no desea poder cambiar el valor copiado de i
en el bucle, puede forzar que el tipo de i
sea const char
esta manera:
for (const auto i: path)
std::cout << i << '' '';
Si desea modificar los elementos en la path
, puede usar una referencia:
for (auto& i: path)
std::cout << i << '' '';
e incluso si no desea modificar la path
, si la copia de objetos es costosa, debe usar una referencia constante en lugar de copiar por valor:
for (const auto& i: path)
std::cout << i << '' '';
std :: copia
Ver la respuesta de Joshua . Puede usar el algoritmo STL std::copy
para copiar el contenido del vector en el flujo de salida. Esta es una solución elegante si se siente cómodo con ella (y además, es muy útil, no solo en este caso de imprimir el contenido de un vector).
std :: for_each
Ver la solución de Max . El uso de std::for_each
es excesivo para este simple escenario, pero es una solución muy útil si desea hacer algo más que imprimir en la pantalla: usar std::for_each
permite realizar cualquier operación (sensible) en los contenidos vectoriales.
sobrecarga ostream :: operador <<
Vea la respuesta de Chris , esto es más un complemento a las otras respuestas, ya que todavía necesitará implementar una de las soluciones anteriores en la sobrecarga. En su ejemplo usó un contador en un bucle for
. Por ejemplo, así es como puedes usar rápidamente la solución de Joshua :
template <typename T>
std::ostream& operator<< (std::ostream& out, const std::vector<T>& v) {
if ( !v.empty() ) {
out << ''['';
std::copy (v.begin(), v.end(), std::ostream_iterator<T>(out, ", "));
out << "/b/b]";
}
return out;
}
El uso de cualquiera de las otras soluciones debe ser sencillo.
conclusión
Cualquiera de las soluciones presentadas aquí funcionará. Depende de usted y el código sobre cuál es el "mejor". Es mejor dejar algo más detallado que esto para otra pregunta donde los pros / contras puedan evaluarse adecuadamente; pero como siempre, la preferencia del usuario siempre tendrá un papel importante: ninguna de las soluciones presentadas es incorrecta, pero algunas se verán mejor para cada codificador individual.
apéndice
Esta es una solución expandida de una anterior que publiqué. Dado que la publicación siguió recibiendo atención, decidí ampliarla y referirme a las otras soluciones excelentes que se publicaron aquí. Mi publicación original tenía un comentario que mencionaba que si pretendía modificar su vector dentro de un bucle for
entonces hay dos métodos proporcionados por std::vector
para acceder a los elementos: std::vector::operator[]
que no hace límites comprobación y std::vector::at
que realiza la comprobación de límites. En otras palabras, arrojará si intenta acceder a un elemento fuera del vector y el operator[]
no lo haría. Solo agregué este comentario, originalmente, por mencionar algo que podría ser útil saber si alguien ya lo hizo. Y no veo diferencia ahora. De ahí este apéndice.
Una forma mucho más fácil de hacer esto es con el algoritmo de copia estándar:
#include <iostream>
#include <algorithm> // for copy
#include <iterator> // for ostream_iterator
#include <vector>
int main() {
/* Set up vector to hold chars a-z */
std::vector<char> path;
for (int ch = ''a''; ch <= ''z''; ++ch)
path.push_back(ch);
/* Print path vector to console */
std::copy(path.begin(), path.end(), std::ostream_iterator<char>(std::cout, " "));
return 0;
}
El ostream_iterator es lo que se llama un adaptador de iterador . Está templado sobre el tipo para imprimir en el flujo (en este caso, char
). cout
(también conocido como salida de consola) es el flujo en el que queremos escribir, y el carácter de espacio ( " "
) es lo que queremos imprimir entre cada elemento almacenado en el vector.
Este algoritmo estándar es poderoso y también lo son muchos otros. El poder y la flexibilidad que te brinda la biblioteca estándar son los que la hacen tan genial. Imagínese: puede imprimir un vector a la consola con solo una línea de código. No tienes que lidiar con casos especiales con el carácter separador. No tienes que preocuparte por los bucles. La biblioteca estándar lo hace todo por ti.
Veo dos problemas. Como se indica en for (x = 17; isalpha(firstsquare); x++)
hay un bucle infinito o nunca se ejecuta en absoluto, y también en if (entrance == ''S'')
si el carácter de entrada es diferente de ''S'' luego no se introduce nada en el vector de ruta, lo que lo deja vacío y, por lo tanto, no imprime nada en la pantalla. Puede probar la última comprobación de path.empty()
o imprimir path.size()
.
De cualquier manera, ¿no sería mejor usar una cadena en lugar de un vector? También puede acceder a los contenidos de la cadena como una matriz, buscar caracteres, extraer subcadenas e imprimir la cadena fácilmente (sin un bucle).
Hacerlo todo con cuerdas podría ser la manera de escribirlo de una manera menos complicada y más fácil de detectar el problema.
operador de sobrecarga <<:
template<typename OutStream, typename T>
OutStream& operator<< (OutStream& out, const vector<T>& v)
{
for (auto const& tmp : v)
out << tmp << " ";
out << endl;
return out;
}
Uso:
vector <int> test {1,2,3};
wcout << test; // or any output stream