web-dev-qa-db-fra.com

Comment imprimer le contenu d'un vecteur?

Je veux imprimer le contenu d'un vecteur en C++, voici ce que j'ai:

#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;
    }
}

Comment imprimer le contenu du vecteur sur l'écran?

242
forthewinwin

Pour répondre à votre question, vous pouvez utiliser un itérateur:

std::vector<char> path;
// ...
for (std::vector<char>::const_iterator i = path.begin(); i != path.end(); ++i)
    std::cout << *i << ' ';

Si vous souhaitez modifier le contenu du vecteur dans la boucle for, utilisez iterator plutôt que const_iterator.

Mais on peut en dire beaucoup plus à ce sujet. Si vous voulez juste une réponse que vous pouvez utiliser, alors vous pouvez vous arrêter ici; sinon, lisez la suite.

auto (C++ 11)/typedef

Ce n'est pas une autre solution, mais un complément à la solution iterator ci-dessus. Si vous utilisez le standard C++ 11 (ou une version ultérieure), vous pouvez utiliser le mot clé auto pour améliorer la lisibilité:

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

Mais le type de i sera non-constant (c’est-à-dire que le compilateur utilisera std::vector<char>::iterator comme type de i).

Dans ce cas, vous pouvez aussi bien utiliser un typedef (non limité à C++ 11, et très utile quand même) :

typedef std::vector<char> Path;
Path path;
// ...
for (Path::const_iterator i = path.begin(); i != path.end(); ++i)
    std::cout << *i << ' ';

compteur

Vous pouvez bien sûr utiliser un type entier pour enregistrer votre position dans la boucle for:

for(int i=0; i<path.size(); ++i)
  std::cout << path[i] << ' ';

Si vous envisagez de le faire, il est préférable d'utiliser les types de membre du conteneur, s'ils sont disponibles et appropriés. std::vector a un type de membre appelé size_type pour ce travail: il s'agit du type renvoyé par la méthode size.

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

Pourquoi ne pas simplement utiliser ceci sur la solution iterator? Pour des cas simples, vous pourriez aussi bien, mais le fait est que la classe iterator est un objet conçu pour effectuer ce travail pour des objets plus complexes, pour lesquels cette solution ne sera pas idéale.

basé sur la plage pour la boucle (C++ 11)

Voir solution de Jefffrey . En C++ 11 (et versions ultérieures), vous pouvez utiliser la nouvelle boucle for basée sur une plage, qui ressemble à ceci:

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

Puisque path est un vecteur d'éléments (explicitement std::vector<char>), l'objet i est du type de l'élément du vecteur (c'est-à-dire explicitement, il est de type char ). L'objet i a une valeur qui est une copie de l'élément réel dans l'objet path. Ainsi, toutes les modifications apportées à i dans la boucle ne sont pas conservées dans path elle-même. De plus, si vous souhaitez imposer le fait que vous ne voulez pas pouvoir modifier la valeur copiée de i dans la boucle, vous pouvez forcer le type de i à être const char comme ça:

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

Si vous souhaitez modifier les éléments dans path, vous pouvez utiliser une référence:

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

et même si vous ne voulez pas modifier path, si la copie d'objets est coûteuse, vous devriez utiliser une référence const au lieu de la copie par valeur:

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

std :: copy

Voir réponse de Josué . Vous pouvez utiliser l'algorithme STL std::copy pour copier le contenu du vecteur sur le flux de sortie. C’est une solution élégante si vous êtes à l’aise (et en plus, c’est très utile , et pas seulement dans le cas où vous imprimez le contenu d’un vecteur ).

std :: for_each

Voir solution de Max . Utiliser std::for_each est excessif pour ce scénario simple, mais c’est une solution très utile si vous voulez faire plus que simplement imprimer à l’écran: utiliser std::for_each vous permet de faire (sensible) opération sur le contenu du vecteur.

surcharge ostream :: operator <<

Voir réponse de Chris , il s’agit plutôt d’un complément aux autres réponses car vous devrez toujours mettre en oeuvre l’une des solutions ci-dessus dans la surcharge. Dans son exemple, il a utilisé un compteur dans une boucle for. Par exemple, voici comment utiliser rapidement solution 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;
}

L'utilisation de l'une des autres solutions doit être simple.

conclusion

Toutes les solutions présentées ici fonctionneront. C'est à vous et au code sur lequel est le "meilleur". Quelque chose de plus détaillé que cela est probablement préférable de laisser pour une autre question où les avantages/inconvénients peuvent être correctement évalués; mais comme toujours les préférences des utilisateurs joueront toujours un rôle: aucune des solutions présentées n’est fausse, mais certaines seront plus jolies pour chaque codeur.

addenda

Ceci est une solution développée de celle que j'avais précédemment publiée. Comme ce message continuait à attirer l'attention, j'ai décidé de l'étoffer et de me référer aux autres excellentes solutions présentées ici. Mon message original mentionnait que si vous vouliez vouloir modifier votre vecteur à l'intérieur d'une boucle for, il existe deux méthodes fournies par std::vector à éléments d'accès: std::vector::operator[] qui ne vérifie pas les limites, et std::vector::at qui effectue la vérification des limites. En d'autres termes, at lancera si vous essayez d'accéder à un élément situé en dehors du vecteur et que operator[] ne le fera pas. J’ai seulement ajouté ce commentaire, à l’origine, dans le seul but de mentionner quelque chose qu’il serait peut-être utile de savoir si quelqu'un le faisait déjà. Et je ne vois aucune différence maintenant. D'où cet addenda.

337
Zorawar

Une méthode beaucoup plus simple consiste à utiliser la norme algorithme de copie :

#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;
}

Ostream_iterator est ce qu'on appelle un adaptateur d'itérateur . Il est modélisé sur le type pour imprimer dans le flux (dans ce cas, char). cout (aka console output) est le flux sur lequel nous voulons écrire, et le caractère d'espace (" ") est ce que nous voulons imprimer entre chaque élément stocké dans le vecteur.

Cet algorithme standard est puissant, de même que de nombreux autres. La puissance et la flexibilité que vous offre la bibliothèque standard sont ce qui la rend si géniale. Imaginez: vous pouvez imprimer un vecteur sur la console avec seulement une ligne de code. Vous n'avez pas à traiter de cas particuliers avec le caractère séparateur. Vous n'avez pas besoin de vous soucier des boucles forçage. La bibliothèque standard fait tout pour vous.

202
Joshua Kravitz

En C++ 11, vous pouvez maintenant utiliser une boucle basée sur une plage for :

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

Je pense que la meilleure façon de faire est de simplement surcharger operator<< en ajoutant cette fonction à votre programme:

#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;
}

Vous pouvez ensuite utiliser l'opérateur << sur n'importe quel vecteur possible, en supposant que ses éléments ont également défini ostream& operator<<:

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;

Les sorties:

{first, second, third}
{1, 0, 1, 0, 0}
{1, 2, 3, 4}
37
Chris Redford

Que diriez-vous de for_each + expression lambda:

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

Bien sûr, un basé sur la plage pour est la solution la plus élégante pour cette tâche concrète, mais celle-ci offre également de nombreuses autres possibilités.

Explication

L'algorithme for_each prend un plage d'entrée et un objet appelable, appelant cet objet sur chaque élément de la plage. Un plage d'entrée est défini par deux itérateurs. Un objet appelable peut être une fonction, un pointeur sur une fonction, un objet d'une classe qui surcharge () operator ou, comme dans ce cas, ne expression lambda. Le paramètre de cette expression correspond au type des éléments du vecteur.

La beauté de cette implémentation réside dans le pouvoir que vous procurent les expressions lambda. Vous pouvez utiliser cette approche pour beaucoup plus de choses que la simple impression du vecteur.

20
Maxim Chetrusca

Il suffit de copier le conteneur sur la console.

std::vector<int> v{1,2,3,4};
std::copy(v.begin(),v.end(),std::ostream_iterator<int>(std::cout, " " ));

Devrait-il produire:

1 2 3 4
10
g24l

Le problème est probablement dans la boucle précédente: (x = 17; isalpha(firstsquare); x++). Cette boucle ne fonctionnera pas du tout (si firstsquare n'est pas alpha) ou sera exécutée indéfiniment (s'il s'agit d'alpha). La raison en est que firstsquare ne change pas car x est incrémenté.

8
MSalters

Utiliser _std::copy_ mais sans séparateur supplémentaire

Une approche alternative/modifiée utilisant std::copy (telle qu'utilisée à l'origine dans @ JoshuaKravtiz answer ) mais sans inclure de séparateur de fin supplémentaire après le dernier élément:

_#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;
}
_

Exemple d'utilisation appliqué au conteneur d'un type de POD personnalisé:

_// 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;
}
_
6
dfri

En C++ 11, une boucle for basée sur une plage peut constituer une bonne solution:

vector<char> items = {'a','b','c'};
for (char n : items)
    cout << n << ' ';

Sortie:

a b c 
6
stupidgoddamnjerk

opérateur de surcharge <<:

template<typename OutStream, typename T>
OutStream& operator<< (OutStream& out, const vector<T>& v)
{
    for (auto const& tmp : v)
        out << tmp << " ";
    out << endl;
    return out;
}

Usage:

vector <int> test {1,2,3};
wcout << test; // or any output stream
3
ivanmara

Je vois deux problèmes. Comme indiqué dans for (x = 17; isalpha(firstsquare); x++), il existe soit une boucle infinie, soit jamais exécutée, et également dans if (entrance == 'S') si le caractère d'entrée est différent de 'S', rien n'est enfoncé dans le vecteur de chemin, le rendant vide. et donc ne rien imprimer à l'écran. Vous pouvez tester cette dernière en vérifiant path.empty() ou en imprimant path.size().

Quoi qu'il en soit, ne serait-il pas préférable d'utiliser une chaîne plutôt qu'un vecteur? Vous pouvez également accéder au contenu de la chaîne comme un tableau, rechercher des caractères, extraire des sous-chaînes et imprimer la chaîne facilement (sans boucle).

Faire tout cela avec des chaînes pourrait être le moyen de l'écrire d'une manière moins compliquée et plus facile à repérer le problème.

3
miguelbernadi

Cette réponse est basée sur la réponse de Zorawar, mais je ne peux pas laisser de commentaire ici.

Vous pouvez faire la version auto (C++ 11)/typedef en utilisant cbegin et cend à la place

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

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] << ' ';
1
Akash Kandpal

Cela a fonctionné pour moi:

    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
Tempi Skat
#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;
}``
}
0
Bishal B Sonar

Vous pouvez écrire votre propre fonction:

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

Pour ceux qui sont intéressés: j'ai écrit une solution généralisée qui prend le meilleur des deux mondes, est plus généralisée à tout type de plage et met des guillemets autour des types non arithmétiques (souhaités pour les types de type chaîne). De plus, cette approche ne devrait pas avoir de problèmes ADL et éviter les "surprises" (puisqu'elle est ajoutée explicitement au cas par cas):

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;
}

Maintenant, il est assez facile à utiliser sur n'importe quelle gamme:

std::cout << range_out{ my_vector };

Le chèque en ficelle laisse place à l'amélioration. J'ai aussi static_assert cocher ma solution pour éviter std::basic_string<>, mais je l'ai laissée ici par souci de simplicité.

0
darune