¿Cómo imprimir el contenido de un vector?


282

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?


1
¿Por qué "no funciona"?
Predeterminado

Respuestas:


394

Solo 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, use en iteratorlugar 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 iteratorsolución anterior . Si está utilizando el estándar C ++ 11 (o posterior), puede usar la autopalabra clave para ayudar a la legibilidad:

for (auto i = path.begin(); i != path.end(); ++i)
    std::cout << *i << ' ';

Pero el tipo de iserá no constante (es decir, el compilador lo usará std::vector<char>::iteratorcomo tipo de i).

En este caso, también podría usar 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 forbucle:

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::vectortiene un tipo de miembro llamado size_typepara este trabajo: es el tipo devuelto por el sizemétodo.

// Path typedef'd to std::vector<char>
for( Path::size_type i=0; i<path.size(); ++i)
  std::cout << path[i] << ' ';

¿Por qué no solo usar esto sobre la iteratorsolución? Para casos simples, también podría iteratorhacerlo , pero el punto es que la clase es un objeto diseñado para hacer este trabajo para objetos más complicados donde esta solución no será ideal.

basado en rango para bucle (C ++ 11)

Ver la solución de Jefffrey . En C ++ 11 (y versiones posteriores) puede usar el nuevo forbucle basado en rango , que se ve así:

for (auto i: path)
  std::cout << i << ' ';

Dado que pathes un vector de elementos (explícitamente std::vector<char>), el objeto ies del tipo del elemento del vector (es decir, explícitamente, es de tipo char). El objeto itiene un valor que es una copia del elemento real en el pathobjeto. Por lo tanto, todos los cambios ien el bucle no se conservan en pathsí mismos. Además, si desea imponer el hecho de que no desea poder cambiar el valor copiado de ien el bucle, puede forzar que el tipo de isea const charasí:

for (const auto i: path)
  std::cout << i << ' ';

Si desea modificar los elementos path, puede usar una referencia:

for (auto& i: path)
  std::cout << i << ' ';

e incluso si no desea modificar 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 :: copy

Ver la respuesta de Joshua . Puede usar el algoritmo STL std::copypara copiar el contenido del vector en la secuencia 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 . Usar std::for_eaches excesivo para este escenario simple, pero es una solución muy útil si desea hacer algo más que imprimir en la pantalla: usar le std::for_eachpermite realizar cualquier operación (sensata) en el contenido del vector.

sobrecarga ostream :: operador <<

Vea la respuesta de Chris , esto es más un complemento de las otras respuestas, ya que aún tendrá que implementar una de las soluciones anteriores en la sobrecarga. En su ejemplo, utilizó un contador en un forbucle. Por ejemplo, así es como podría 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 del código en cuál es el "mejor". Probablemente sea mejor dejar algo más detallado que esto para otra pregunta en la que los pros / contras puedan evaluarse adecuadamente; pero, como siempre, la preferencia del usuario siempre jugará un papel: ninguna de las soluciones presentadas es incorrecta, pero algunas se verán mejor para cada codificador individual.

apéndice

Esta es una solución ampliada de una anterior que publiqué. Como esa publicación seguía recibiendo atención, decidí ampliarla y referirme a las otras excelentes soluciones que se publicaron aquí. Mi publicación original tenía un comentario que mencionaba que si tenía la intención de modificar su vector dentro de un forbucle, entonces hay dos métodos proporcionados std::vectorpara acceder a los elementos: std::vector::operator[]que no realiza la comprobación de límites y std::vector::atque realiza la comprobación de límites. En otras palabras, atse lanzará si intenta acceder a un elemento fuera del vector y operator[]no lo haría. Solo agregué este comentario, originalmente, para mencionar algo que podría ser útil saber si alguien ya no lo hizo. Y no veo diferencia ahora. De ahí esta adición.


Si está bucle de 0través vector::size()y el vector no se modifica dentro del bucle no hay necesidad de usar at()e incurrir en los límites de las comprobaciones de gastos generales. Dicho esto, iría con un iterador como sugieres.
Ed S.

1
@Ed: sí, no tiene sentido usar atsi nada en el bucle modifica el vector, pero pensé en mencionarlo solo en caso de que el vector se modifique en el bucle (sin que se recomiende) y porque nunca recibe un mencionar y podría ser útil, al menos, saberlo.
Zorawar

El bucle for basado en rango podría reescribirse para usar referencias, que pueden ser importantes en el caso de subobjetos grandes, como sigue:for (auto const &i: path) std::cout << i << ' ';
underscore_d

@underscore_d: gracias. He limpiado esa sección y espero que sea más completa y un poco más clara ahora.
Zorawar

"operador de sobrecarga <<" no es una buena solución; al menos un operando de operador sobrecargado debe ser una clase definida por su programa, debido a la búsqueda dependiente de argumentos
MM

218

Una manera 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 iterador . Se crea una plantilla sobre el tipo para imprimir en la secuencia (en este caso, char). cout(también conocido como salida de consola) es la secuencia en la 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 muchos otros también. El poder y la flexibilidad que le brinda la biblioteca estándar son los que la hacen tan grandiosa. Imagínese: puede imprimir un vector en la consola con solo una línea de código. No tiene que tratar casos especiales con el carácter separador. No necesita preocuparse por los bucles for. La biblioteca estándar lo hace todo por usted.


3
¿Y si mi vector fuera de tipo vector<pair<int, struct node>>? ¿Cómo uso el método anterior para imprimir este vector?
mtk

66
La cadena delimitador se escribe después de cada elemento, no entre ellos, es decir, también después del último. Eso puede requerir tratar con casos especiales si solo lo quiere en medio, es decir, como un separador.
Quigi

2
@mtk puede declarar una operator<<función para su par específico <>.
ShoeLace

Se agregó una respuesta que muestra un enfoque similar, pero teniendo en cuenta el comentario de @Quigi arriba, con respecto al separador final adicional.
dfri

@ShoeLace ¿No hay otra manera?
thegreatcoder

69

En C ++ 11 ahora puede usar un bucle for basado en rango :

for (auto const& c : path)
    std::cout << c << ' ';

Esto funciona muy bien solo si el tamaño del vector no cambia en el cuerpo del rango para el bucle.
Brian

8
@BrianP. Sip. La impresión de los elementos de un contenedor no modifica el rango del contenedor.
Zapato

¿Qué es preferible aquí: c como una copia de valor o como una referencia constante para evitar copiar el elemento?
Kleinfreund

@kleinfreund Depende del contenido del vector. Por ejemplo, para un vector de chars, es probable que pasar por referencia constante sea realmente más costoso que por valor. Pero aquí estamos hablando de súper micro optimizaciones.
Zapato

43

Creo que la mejor manera de hacer esto es simplemente sobrecargar 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, suponiendo que sus elementos también hayan 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}

3
Almacenar v.size () - 1 como int es una posible pérdida de precisión. Arreglé esto en una edición revisada por pares aceptada ( stackoverflow.com/revisions/23397700/5 ), pero luego se editó nuevamente restaurando la posible pérdida de precisión. Supongo que no importa demasiado en la práctica, ya que los vectores no suelen ser tan grandes.
JDiMatteo

No almacenarlo como una variable disminuye la legibilidad del código, que es una parte de su edición con la que no estoy de acuerdo. He cambiado el tipo de lasta size_t.
Chris Redford

size_t last = v.size() - 1;parece redundante, puede usar if (i) out << ", ";condición antes del out << v[i]; enlace
Vladimir Gamalyan

3
ADL no encuentra este operador, ya que no está en el espacio de nombres de ninguno de sus argumentos. Por lo tanto, estará oculto por cualquier otro espacio de nombres operator<<. Ejemplo
MM

Si vas a hacer esto, ¿por qué probar if (i != last) cada vez en el ciclo? En cambio, si el contenedor no está vacío, entonces (a) envíe el primer elemento, y luego (b) envíe en bucle los elementos restantes , imprimiendo primero el separador (como prefijo). No se requiere prueba de bucle interno (aparte de la condición del bucle en sí). Solo se requiere una prueba fuera de circuito.
WhozCraig

22

¿Qué tal for_each+ expresión lambda :

#include <vector>
#include <algorithm>
...
std::vector<char> vec;
...
std::for_each(
              vec.cbegin(),
              vec.cend(),
              [] (const char c) {std::cout << c << " ";} 
              );
...

Por supuesto, una solución basada en el rango es la solución más elegante para esta tarea concreta, pero esta también ofrece muchas otras posibilidades.

Explicación

El for_eachalgoritmo toma un rango de entrada y un objeto invocable , llamando a este objeto en cada elemento del rango. Un rango de entrada está definido por dos iteradores . Un objeto invocable puede ser una función, un puntero a la función, un objeto de una clase que se sobrecarga () operatoro, como en este caso, una expresión lambda . El parámetro para esta expresión coincide con el tipo de elementos del vector.

La belleza de esta implementación es el poder que obtienes de las expresiones lambda: puedes usar este enfoque para muchas más cosas que solo imprimir el vector.


11

Simplemente copie 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, " " ));

Debería dar salida:

1 2 3 4

8

El problema es, probablemente, en el bucle anterior: (x = 17; isalpha(firstsquare); x++). Este bucle no se ejecutará en absoluto (si firstsquareno es alfa) o se ejecutará para siempre (si es alfa). La razón es que firstsquareno cambia a medida que xse incrementa.


7

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 

6

Uso std::copypero sin separador posterior adicional

Un enfoque alternativo / modificado que usa std::copy(como se usó originalmente en la respuesta @JoshuaKravtiz ) pero sin incluir un separador final 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;
}

5

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

3

Veo dos problemas Como se señaló, for (x = 17; isalpha(firstsquare); x++)hay un bucle infinito o nunca se ejecuta en absoluto, y también if (entrance == 'S')si el carácter de entrada es diferente a 'S', entonces nada se empuja al vector de ruta, lo que lo deja vacío y, por lo tanto, no imprime nada en la pantalla. Puede probar este último buscando path.empty()o imprimiendo path.size().

De cualquier manera, ¿no sería mejor usar una cadena en lugar de un vector? También puede acceder al contenido de la cadena como una matriz, buscar caracteres, extraer subcadenas e imprimir la cadena fácilmente (sin bucle).

Hacerlo todo con cadenas podría ser la forma de escribirlo de una manera menos complicada y más fácil de detectar el problema.


3

Esta respuesta se basa en la respuesta de Zorawar, pero no pude dejar un comentario allí.

Puede hacer que la versión automática (C ++ 11) / typedef sea constante usando cbegin y cend en su lugar

for (auto i = path.cbegin(); i != path.cend(); ++i)
    std::cout << *i << ' ';

1

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 no proporciona ninguna información adicional en comparación con las respuestas ya existentes.
Yashas

0
#include<bits/stdc++.h>
using namespace std;

int main()
{
    vector <pair<string,int> > v;
    int n;
    cin>>n;
int i;
    for( i=0;i<n;i++)
    {
        int  end;
        string str;
        cin>>str;
        cin>>end;
        v.push_back(make_pair(str,end));
    }



for (int j=0;j<n;j++)
{
    cout<<v[j].first<< " "<<v[j].second<<endl;
}``
}

2
¡Hola! Bienvenido a stackoverflow. Sería genial si pudiera incluir con el fragmento de código alguna explicación de lo que está haciendo y cómo responde a la pregunta.
Slabgorb


0

Esto funcionó para mí:

    for (auto& i : name)
    {
    int r = 0;
    for (int j = 0; j < name[r].size();j++) 
    {
    std::cout << i[j];
    }
    r++;
    std::cout << std::endl;
    }

0

Para aquellos que estén interesados: escribí una solución generalizada que toma lo mejor de ambos mundos, está más generalizada a cualquier tipo de rango y pone comillas alrededor de los tipos no aritméticos (deseados para los tipos similares a cadenas). Además, este enfoque no debería tener ningún problema de ADL y también evitar 'sorpresas' (ya que se agrega explícitamente caso por caso):

template <typename T>
inline constexpr bool is_string_type_v = std::is_convertible_v<const T&, std::string_view>;

template<class T>
struct range_out {
  range_out(T& range) : r_(range) {
  }
  T& r_;
  static_assert(!::is_string_type_v<T>, "strings and string-like types should use operator << directly");
};

template <typename T>
std::ostream& operator<< (std::ostream& out, range_out<T>& range) {
  constexpr bool is_string_like = is_string_type_v<T::value_type>;
  constexpr std::string_view sep{ is_string_like ? "', '" : ", " };

  if (!range.r_.empty()) {
    out << (is_string_like ? "['" : "[");
    out << *range.r_.begin();
    for (auto it = range.r_.begin() + 1; it != range.r_.end(); ++it) {
      out << sep << *it;
    }
    out << (is_string_like ? "']" : "]");
  }
  else {
    out << "[]";
  }

  return out;
}

Ahora es bastante fácil de usar en cualquier rango:

std::cout << range_out{ my_vector };

El cheque en forma de cuerda deja margen para mejorar. También tengo que static_assertverificar mi solución para evitarla std::basic_string<>, pero la dejé aquí por simplicidad.


-1

Puedes escribir tu propia función:

void printVec(vector<char> vec){
    for(int i = 0; i < vec.size(); i++){
        cout << vec[i] << " ";
    }
    cout << endl;
}

-1

Para las personas que quieren líneas simples sin bucles:

No puedo creer que nadie haya pensado en esto, pero tal vez sea por el enfoque más parecido a C. De todos modos, es perfectamente seguro hacer esto sin un bucle, de una sola línea, suponiendo que std::vector<char>está terminado en nulo:

std::vector<char> test { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'w', 'o', 'r', 'l', 'd', '!', '\0' };
std::cout << test.data() << std::endl;

Pero lo envolvería en el ostreamoperador, como sugirió @Zorawar, solo para estar seguro:

template <typename T>std::ostream& operator<< (std::ostream& out, std::vector<T>& v)
{
    v.push_back('\0'); // safety-check!
    out << v.data();
    return out;
}

std::cout << test << std::endl; // will print 'Hello, world!'

Podemos lograr un comportamiento similar usando en su printflugar:

fprintf(stdout, "%s\n", &test[0]); // will also print 'Hello, world!'

NOTA:

El ostreamoperador sobrecargado debe aceptar el vector como no constante. Esto podría hacer que el programa sea inseguro o introducir código inutilizable. Además, dado que se agrega un carácter nulo, std::vectorpuede producirse una reasignación de . Por lo tanto, usar for-loops con iteradores probablemente será más rápido.


1
1. fprintf(stdout, "%s\n", &test[0]);no es diferente de std::cout << test.data(), ambos requieren un vector con terminación nula. 2. "Pero envolvería esto en el operador ostream operator" << que modifica el operando correcto es una muy mala idea.
HolyBlackCat

Lo he usado fprintf(stdout, "%s\n", &test[0]);durante mucho tiempo en código sin que me haya causado ningún problema. ¡Interesante! Y estoy de acuerdo en que no es tan agradable modificar el vector en el ostreamoperador, pero no me gusta tanto el bucle manual como el uso de iteradores. De alguna manera siento que para operaciones simples como imprimir una std::vector<char>biblioteca estándar debería ocultar estas cosas. Pero C ++ se está desarrollando constantemente, podría llegar pronto.
ワ イ き ん ぐ
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.