Comment imprimer le contenu d'un vecteur?

282

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 à l'écran?

forthewinwin
la source
1
pourquoi ça "ne marche pas"?
Par défaut

Réponses:

394

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 iteratorplutôt que const_iterator.

Mais il y a beaucoup plus à dire à ce sujet. Si vous voulez juste une réponse que vous pouvez utiliser, 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 iteratorsolution ci-dessus . Si vous utilisez la norme C ++ 11 (ou ultérieure), vous pouvez utiliser le automot clé pour améliorer la lisibilité:

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

Mais le type de isera non-const (ie, le compilateur utilisera std::vector<char>::iteratorcomme type de i).

Dans ce cas, vous pourriez tout aussi bien utiliser un typedef(non limité à C ++ 11, et très utile à utiliser 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 forboucle:

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

Si vous comptez le faire, il est préférable d'utiliser les types de membres du conteneur, s'ils sont disponibles et appropriés. std::vectora un type de membre appelé size_typepour ce travail: c'est le type renvoyé par la sizeméthode.

// 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 l'utiliser sur la iteratorsolution? Pour les cas simples, vous pourriez aussi bien, mais le fait est que la iteratorclasse est un objet conçu pour faire ce travail pour des objets plus compliqués où cette solution ne sera pas idéale.

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

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

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

Puisque pathest un vecteur d'éléments (explicitement std::vector<char>), l'objet iest de type de l'élément du vecteur (c'est-à-dire, explicitement, il est de type char). L'objet ia une valeur qui est une copie de l'élément réel dans l' pathobjet. Ainsi, toutes les modifications apportées à ila boucle ne sont pas conservées en pathsoi. De plus, si vous voulez faire respecter le fait que vous ne voulez pas être en mesure de changer la valeur copiée de ila boucle, vous pouvez forcer le type de iêtre const charcomme ceci:

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 souhaitez pas modifier path, si la copie d'objets est coûteuse, vous devez utiliser une référence const au lieu de copier par valeur:

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

std :: copy

Voir la réponse de Joshua . Vous pouvez utiliser l'algorithme STL std::copypour copier le contenu vectoriel sur le flux de sortie. C'est une solution élégante si vous êtes à l'aise avec elle (et d'ailleurs, elle est très utile, pas seulement dans ce cas d'impression du contenu d'un vecteur).

std :: for_each

Voir la solution de Max . L'utilisation std::for_eachest exagérée pour ce scénario simple, mais c'est une solution très utile si vous vouliez faire plus que simplement imprimer à l'écran: l'utilisation std::for_eachvous permet d'effectuer n'importe quelle opération (raisonnable) sur le contenu du vecteur.

surcharge ostream :: operator <<

Voir la réponse de Chris , c'est plus un complément aux autres réponses puisque vous devrez toujours implémenter l'une des solutions ci-dessus dans la surcharge. Dans son exemple, il a utilisé un compteur en forboucle. Par exemple, voici comment utiliser rapidement la 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 on est le "meilleur". Tout ce qui est plus détaillé est probablement préférable de laisser une autre question où les avantages / inconvénients peuvent être correctement évalués; mais comme toujours, la préférence de l'utilisateur jouera toujours un rôle: aucune des solutions présentées n'est fausse, mais certaines seront plus jolies pour chaque codeur individuel.

Addenda

Il s'agit d'une solution étendue d'une précédente que j'ai publiée. Étant donné que ce message continuait à attirer l'attention, j'ai décidé de l'étendre et de faire référence aux autres excellentes solutions publiées ici. Mon message original avait une remarque qui dit que si vous aviez l' intention sur la modification de votre vecteur dans une forboucle , puis il y a deux méthodes fournies par std::vectordes éléments d'accès: std::vector::operator[]qui ne fait pas vérification de limites, et std::vector::atqui n'effectue la vérification des limites. En d'autres termes, atlancera si vous essayez d'accéder à un élément en dehors du vecteur et operator[]ne le ferait pas. Je n'ai ajouté ce commentaire, à l'origine, que pour mentionner quelque chose qu'il pourrait être utile de savoir si quelqu'un ne l'a pas déjà fait. Et je ne vois aucune différence maintenant. D'où cet addendum.

Zorawar
la source
Si vous effectuez une boucle de part en 0part vector::size()et que le vecteur n'est pas modifié dans la boucle, il n'est pas nécessaire d'utiliser at()et d'encourir les frais supplémentaires de vérification des limites. Cela dit, j'irais avec un itérateur comme vous le suggérez.
Ed S.
1
@Ed: oui, il n'y a aucun intérêt à utiliser atsi rien dans la boucle ne modifie le vecteur, mais j'ai pensé le mentionner juste au cas où le vecteur serait modifié dans la boucle (non recommandé que cela puisse être) et parce qu'il n'obtient jamais de mentionner et il pourrait être utile, au moins, de le savoir.
Zorawar
La boucle for basée sur une plage peut être réécrite pour utiliser des références, ce qui peut être important dans le cas de sous-objets volumineux, comme suit:for (auto const &i: path) std::cout << i << ' ';
underscore_d
@underscore_d: merci. J'ai nettoyé cette section et j'espère qu'elle est à la fois plus complète et un peu plus claire maintenant.
Zorawar
"l'opérateur de surcharge <<" n'est pas une bonne solution; au moins un opérande d'opérateur surchargé doit être une classe définie par votre programme, en raison de la recherche dépendante de l'argument
MM
218

Un moyen beaucoup plus simple de le faire est d'utiliser l' algorithme de copie standard :

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

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

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

Joshua Kravitz
la source
3
et si mon vecteur était de type vector<pair<int, struct node>>. Comment utiliser la méthode ci-dessus pour imprimer ce vecteur?
mtk
6
La chaîne de délimitation est écrite après chaque élément, pas entre, c'est-à-dire également après le dernier. Cela peut nécessiter de traiter des cas spéciaux si vous ne le souhaitez qu'entre, c'est-à-dire comme séparateur.
Quigi
2
@mtk vous pouvez déclarer une operator<<fonction pour votre paire spécifique <>.
ShoeLace
Ajout d' une réponse montrant une approche similaire mais prenant en compte le commentaire de @Quigi: s ci-dessus, concernant le séparateur de fin supplémentaire.
dfri
@ShoeLace N'y a-t-il pas d'autre moyen?
thegreatcoder
69

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

for (auto const& c : path)
    std::cout << c << ' ';
Chaussure
la source
Cela fonctionne très bien uniquement si la taille du vecteur n'est pas modifiée dans le corps de la plage de boucle.
Brian
8
@BrianP. Ouaip. L'impression des éléments d'un conteneur ne modifie pas la plage du conteneur.
Shoe
Qu'est-ce qui est préférable ici - c comme copie de valeur ou comme référence const pour éviter la copie de l'élément?
kleinfreund
@kleinfreund Cela dépend du contenu du vecteur. Par exemple, pour un vecteur de chars, il est probable que le passage par référence constante soit en réalité plus coûteux que par valeur. Mais nous parlons ici de super micro optimisations.
Chaussure
43

Je pense que la meilleure façon de le 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;
}

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

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}
Chris Redford
la source
3
Le stockage de v.size () - 1 en tant qu'int est une perte de précision possible. J'ai corrigé cela dans une modification approuvée par les pairs ( stackoverflow.com/revisions/23397700/5 ), mais elle a ensuite été modifiée à nouveau, rétablissant la perte de précision possible. Je suppose que cela n'a pas trop d'importance dans la pratique, car les vecteurs ne sont généralement pas si gros.
JDiMatteo
Ne pas le stocker en tant que variable diminue la lisibilité du code, ce qui est une partie de votre modification avec laquelle je n'étais pas d'accord. J'ai changé le type de lastà size_t.
Chris Redford
size_t last = v.size() - 1;semble redondant, vous pouvez utiliser la if (i) out << ", ";condition avant le out << v[i]; lien
Vladimir Gamalyan
3
Cet opérateur n'est pas trouvé par ADL, car il ne se trouve dans l'espace de noms d'aucun de ses arguments. Il sera donc masqué par tout autre espace de noms operator<<. Exemple
MM
Si vous allez faire cela, pourquoi tester if (i != last) chaque fois dans la boucle? Au lieu de cela, si le conteneur ne soit pas vide , alors (a) envoyer le premier élément, puis (b) envoyer en boucle les autres éléments, l' impression du séparateur première (comme un préfixe). Aucun test de boucle interne (à l'exception de la condition de boucle elle-même) n'est requis. Un seul test hors boucle est requis.
WhozCraig
22

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, une gamme basée sur 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' for_eachalgorithme prend une plage d'entrée et un objet appelable , appelant cet objet sur chaque élément de la plage. Une plage d'entrée est définie par deux itérateurs . Un objet appelable peut être une fonction, un pointeur vers une fonction, un objet d'une classe qui surcharge () operatorou comme dans ce cas, une expression lambda . Le paramètre de cette expression correspond au type des éléments du vecteur.

La beauté de cette implémentation est la puissance que vous obtenez des expressions lambda - vous pouvez utiliser cette approche pour beaucoup plus de choses que simplement imprimer le vecteur.

Maxim Chetrusca
la source
11

Copiez simplement 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 produire:

1 2 3 4
g24l
la source
8

Le problème est probablement dans la boucle précédente: (x = 17; isalpha(firstsquare); x++). Cette boucle ne fonctionnera pas du tout (si elle firstsquaren'est pas alpha) ou s'exécutera pour toujours (si elle est alpha). La raison en est que firstsquarecela ne change pas car il xest incrémenté.

MSalters
la source
7

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

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

Production:

a b c 
stupidgoddamnjerk
la source
6

Utilisation std::copymais sans séparateur arrière supplémentaire

Une approche alternative / modifiée utilisant std::copy(comme à l'origine utilisée dans la réponse @JoshuaKravtiz ) mais sans inclure un 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ée au conteneur d'un type 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;
}
dfri
la source
5

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
ivanmara
la source
3

Je vois deux problèmes. Comme indiqué dans for (x = 17; isalpha(firstsquare); x++)il y a soit une boucle infinie ou jamais exécutée du tout, et aussi if (entrance == 'S')si le caractère d'entrée est différent de `` S '', alors rien n'est poussé vers le vecteur de chemin, le rendant vide et n'imprimant donc rien à l'écran. Vous pouvez tester cette dernière vérification path.empty()ou impression path.size().

Quoi qu'il en soit, ne serait-il pas préférable d'utiliser une chaîne au lieu d'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).

Tout faire avec des chaînes pourrait être le moyen de l'écrire de manière moins compliquée et de repérer plus facilement le problème.

miguelbernadi
la source
3

Cette réponse est basée sur la réponse de Zorawar, mais je n'ai pas pu laisser de commentaire là-bas.

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

for (auto i = path.cbegin(); i != path.cend(); ++i)
    std::cout << *i << ' ';
SketchyManDan
la source
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] << ' ';
Akash Kandpal
la source
Cette réponse ne donne aucune information supplémentaire par rapport aux réponses déjà existantes.
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;
}``
}
Sonar Bishal B
la source
2
Salut! Bienvenue sur stackoverflow. Ce serait formidable si vous pouviez inclure dans l'extrait de code une explication de ce que vous faites et comment cela répond à la question.
Slabgorb
2
Vous voudrez peut-être lire: Pourquoi ne devrais-je pas #inclure <bits / stdc ++. H>?
Blastfurnace
0

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;
    }
Tempi Skat
la source
0

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 également les «surprises» (car 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 contrôle en forme de chaîne laisse place à amélioration. Je dois également static_assertvérifier ma solution pour éviter std::basic_string<>, mais je l'ai laissée ici pour plus de simplicité.

Darune
la source
-1

Vous pouvez écrire votre propre fonction:

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

Pour les personnes qui souhaitent une ligne sans boucles:

Je ne peux pas croire que personne n'ait pensé à cela, mais c'est peut-être à cause de l'approche plus semblable à C. Quoi qu'il en soit, il est parfaitement sûr de le faire sans boucle, dans une ligne, en supposant que le terminalstd::vector<char> est nul:

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

Mais je voudrais envelopper cela dans l' ostreamopérateur, comme l'a suggéré @Zorawar, juste pour être sûr:

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!'

Nous pouvons obtenir un comportement similaire en utilisant à la printfplace:

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

REMARQUE:

L' ostreamopérateur surchargé doit accepter le vecteur comme non-const. Cela pourrait rendre le programme non sécurisé ou introduire du code mal utilisé. De plus, comme le caractère nul est ajouté, une réallocation de la std::vectorpeut se produire. Ainsi, l'utilisation de boucles for avec des itérateurs sera probablement plus rapide.

ワ イ き ん ぐ
la source
1
1. fprintf(stdout, "%s\n", &test[0]);n'est pas différent de std::cout << test.data(), les deux nécessitent un vecteur à terminaison nulle. 2. L'opérateur "Mais je voudrais envelopper cela dans l'opérateur ostream" << qui modifie l'opérande droit est une très mauvaise idée.
HolyBlackCat
J'utilise fprintf(stdout, "%s\n", &test[0]);depuis longtemps le code sans que cela ne me pose aucun problème. Intéressant! Et je suis d'accord que ce n'est pas si agréable de modifier le vecteur dans l' ostreamopérateur, mais je n'aime pas à la fois le bouclage manuel et l' utilisation d'itérateurs. D'une certaine manière, je pense que pour des opérations simples comme l'impression d'une std::vector<char>bibliothèque standard, cela devrait cacher ces choses. Mais C ++ se développe constamment, il pourrait arriver bientôt.
ワ イ き ん ぐ