Come stampare il contenuto di un vettore?


282

Voglio stampare il contenuto di un vettore in C ++, ecco cosa ho:

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

Come posso stampare il contenuto del vettore sullo schermo?


1
perché "non funziona"?
Predefinito

Risposte:


394

Solo per rispondere alla tua domanda, puoi usare un iteratore:

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

Se si desidera modificare il contenuto del vettore nel ciclo for, utilizzare iteratorinvece di const_iterator.

Ma c'è molto altro da dire al riguardo. Se vuoi solo una risposta che puoi usare, puoi fermarti qui; altrimenti, continua a leggere.

auto (C ++ 11) / typedef

Questa non è un'altra soluzione, ma un supplemento alla iteratorsoluzione sopra . Se si utilizza lo standard C ++ 11 (o successivo), è possibile utilizzare la autoparola chiave per facilitare la leggibilità:

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

Ma il tipo di inon sarà const (ovvero, il compilatore utilizzerà std::vector<char>::iteratorcome tipo di i).

In questo caso, potresti anche usare solo un typedef(non limitato a C ++ 11, e molto utile da usare comunque):

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

contatore

Ovviamente puoi usare un tipo intero per registrare la tua posizione nel forloop:

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

Se hai intenzione di farlo, è meglio usare i tipi di membri del contenitore, se disponibili e appropriati. std::vectorha un tipo di membro chiamato size_typeper questo lavoro: è il tipo restituito dal sizemetodo.

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

Perché non utilizzarlo solo sulla iteratorsoluzione? Per casi semplici potresti anche, ma il punto è che la iteratorclasse è un oggetto progettato per fare questo lavoro per oggetti più complicati in cui questa soluzione non sarà ideale.

basato su intervallo per loop (C ++ 11)

Vedi la soluzione di Jefffrey . In C ++ 11 (e versioni successive) è possibile utilizzare il nuovo forciclo basato su intervallo , che assomiglia a questo:

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

Poiché pathè un vettore di elementi (esplicitamente std::vector<char>), l'oggetto iè di tipo dell'articolo del vettore (cioè, esplicitamente, è di tipo char). L'oggetto iha un valore che è una copia dell'elemento effettivo pathnell'oggetto. Pertanto, tutte le modifiche a inel ciclo non vengono conservate in pathsé. Inoltre, se si desidera applicare il fatto che non si desidera poter modificare il valore copiato inel ciclo, è possibile forzare il tipo di iessere in const charquesto modo:

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

Se desideri modificare gli elementi in path, puoi utilizzare un riferimento:

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

e anche se non vuoi modificare path, se la copia di oggetti è costosa dovresti usare un riferimento const invece di copiare per valore:

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

std :: copia

Vedi la risposta di Giosuè . È possibile utilizzare l'algoritmo STL std::copyper copiare i contenuti vettoriali sul flusso di output. Questa è una soluzione elegante se ti senti a tuo agio (e inoltre, è molto utile, non solo in questo caso di stampa del contenuto di un vettore).

std :: for_each

Vedi la soluzione di Max . L'utilizzo std::for_eachè eccessivo per questo semplice scenario, ma è una soluzione molto utile se si desidera fare qualcosa di più della semplice stampa su schermo: l'utilizzo std::for_eachconsente di eseguire qualsiasi operazione (ragionevole) sui contenuti vettoriali.

sovraccarico ostream :: operatore <<

Vedi la risposta di Chris , questo è più un complemento delle altre risposte poiché dovrai ancora implementare una delle soluzioni sopra nel sovraccarico. Nel suo esempio ha usato un contatore in un forciclo. Ad esempio, ecco come è possibile utilizzare rapidamente la soluzione di 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'uso di una qualsiasi delle altre soluzioni dovrebbe essere semplice.

conclusione

Qualsiasi delle soluzioni presentate qui funzionerà. Dipende da te e dal codice su cui si è il "migliore". Qualcosa di più dettagliato di questo è probabilmente meglio lasciare per un'altra domanda in cui i pro / contro possono essere adeguatamente valutati; ma come sempre le preferenze dell'utente avranno sempre un ruolo: nessuna delle soluzioni presentate è sbagliata, ma alcune appariranno più belle per ogni singolo programmatore.

appendice

Questa è una soluzione estesa di una precedente che ho pubblicato. Dal momento che quel post ha continuato a ricevere attenzione, ho deciso di ampliarlo e fare riferimento alle altre eccellenti soluzioni che sono state pubblicate qui. Il mio post originale aveva un'osservazione che ha detto che se fossi intenzione sulla modifica vettore all'interno di un forciclo, allora ci sono due metodi forniti da std::vectorad elementi di accesso: std::vector::operator[]che non fa il controllo dei limiti, e std::vector::atche fa eseguire il controllo dei limiti. In altre parole, atverrà lanciato se si tenta di accedere a un elemento esterno al vettore e operator[]non lo farebbe. Ho solo aggiunto questo commento, in origine, per citare qualcosa che potrebbe essere utile sapere se qualcuno lo avesse già fatto. E non vedo alcuna differenza ora. Da qui questo addendum.


Se esegui il looping da 0attraverso vector::size()e il vettore non viene modificato all'interno del loop, non è necessario utilizzare at()e sostenere i limiti extra che controllano l'overhead. Detto questo, vorrei andare con un iteratore come suggerisci tu.
Ed S.

1
@Ed: sì, non ha senso utilizzare atnulla nel loop per modificare il vettore, ma ho pensato di menzionarlo nel caso in cui il vettore venga modificato nel loop (non consigliato come potrebbe essere) e perché non ottiene mai un menzione e potrebbe essere utile, almeno, conoscerlo.
Zorawar,

Il ciclo basato su intervallo può essere riscritto per utilizzare riferimenti, che possono essere importanti in caso di oggetti secondari di grandi dimensioni, come segue:for (auto const &i: path) std::cout << i << ' ';
underscore_d

@underscore_d: grazie. Ho ripulito quella sezione e spero che sia sia più completo che un po 'più chiaro ora.
Zorawar,

"operatore di sovraccarico <<" non è una buona soluzione; almeno un operando di operatore sovraccarico dovrebbe essere una classe definita dal programma, a causa della ricerca dipendente dall'argomento
MM

218

Un modo molto più semplice per farlo è con l' algoritmo di copia 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;
}

Ostream_iterator è ciò che viene chiamato un adattatore iteratore . È templatizzato sul tipo da stampare sullo stream (in questo caso, char). cout(ovvero l'output della console) è il flusso su cui vogliamo scrivere e il carattere spazio ( " ") è ciò che vogliamo stampare tra ogni elemento memorizzato nel vettore.

Questo algoritmo standard è potente e lo sono anche molti altri. La potenza e la flessibilità che la libreria standard ti offre sono ciò che la rende così eccezionale. Immagina: puoi stampare un vettore sulla console con una sola riga di codice. Non devi affrontare casi speciali con il carattere separatore. Non devi preoccuparti dei for-loop. La libreria standard fa tutto per te.


3
e se il mio vettore fosse di tipo vector<pair<int, struct node>>. Come posso usare il metodo sopra per stampare questo vettore?
mtk,

6
La stringa del delimitatore è scritta dopo ogni elemento, non tra, cioè anche dopo l'ultimo. Ciò potrebbe richiedere la gestione di casi speciali se lo desideri solo tra, ad esempio, come separatore.
Quigi,

2
@mtk puoi dichiarare una operator<<funzione per la tua coppia specifica <>.
ShoeLace,

Aggiunta una risposta che mostra un approccio simile ma prendendo in considerazione il commento di @Quigi: s sopra, riguardo al separatore finale aggiuntivo.
venerdì

@ShoeLace Non c'è altro modo?
thegreatcoder,

69

In C ++ 11 ora puoi usare un loop basato su range :

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

Funziona alla grande solo se la dimensione del vettore non viene modificata nel corpo dell'intervallo per il loop.
Brian,

8
@BrianP. Sì. La stampa degli elementi di un contenitore non modifica l'intervallo del contenitore.
Scarpa

Cosa è preferibile qui - c come copia di valore o come riferimento const per evitare di copiare l'elemento?
Kleinfreund,

@kleinfreund Dipende dal contenuto del vettore. Ad esempio, per un vettore di chars, è probabile che passare per riferimento costante sia effettivamente più costoso che per valore. Ma qui stiamo parlando di super micro ottimizzazioni.
Scarpa,

43

Penso che il modo migliore per farlo sia semplicemente sovraccaricare operator<<aggiungendo questa funzione al tuo programma:

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

Quindi è possibile utilizzare l' <<operatore su qualsiasi possibile vettore, supponendo che anche i suoi elementi abbiano ostream& operator<<definito:

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;

Uscite:

{first, second, third}
{1, 0, 1, 0, 0}
{1, 2, 3, 4}

3
La memorizzazione di v.size () - 1 come int è una possibile perdita di precisione. Ho risolto questo problema in una modifica accettata peer review ( stackoverflow.com/revisions/23397700/5 ), ma in seguito è stata nuovamente modificata ripristinando la possibile perdita di precisione. Immagino che non abbia molta importanza in pratica poiché i vettori non sono di solito così grandi.
JDiMatteo,

Non memorizzarlo come variabile diminuisce la leggibilità del codice, che è una parte della modifica con cui non sono d'accordo. Ho cambiato il tipo di lasta size_t.
Chris Redford,

size_t last = v.size() - 1;sembra ridondante, è possibile utilizzare la if (i) out << ", ";condizione prima del out << v[i]; collegamento
Vladimir Gamalyan,

3
Questo operatore non viene trovato da ADL, poiché non si trova nello spazio dei nomi di nessuno dei suoi argomenti. Quindi sarà nascosto da qualsiasi altro spazio dei nomi operator<<. Esempio
MM

Se hai intenzione di farlo, perché testare if (i != last) ogni volta nel loop? Invece, se il contenitore non è vuoto, quindi (a) invia il primo elemento, quindi (b) invia in sequenza gli elementi rimanenti , stampando prima il separatore (come prefisso). Non è richiesto alcun test del loop interno (a parte la condizione del loop stesso). È richiesto un solo test out-of-loop.
WhozCraig

22

Che ne dite di for_each+ espressione lambda :

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

Naturalmente, una gamma basata su è la soluzione più elegante per questo compito concreto, ma questa offre anche molte altre possibilità.

Spiegazione

L' for_eachalgoritmo accetta un intervallo di input e un oggetto richiamabile , chiamando questo oggetto su ogni elemento dell'intervallo. Un intervallo di input è definito da due iteratori . Un oggetto richiamabile può essere una funzione, un puntatore alla funzione, un oggetto di una classe che sovraccarica () operatoro, come in questo caso, un'espressione lambda . Il parametro per questa espressione corrisponde al tipo di elementi dal vettore.

Il bello di questa implementazione è il potere che ottieni dalle espressioni lambda: puoi usare questo approccio per molte più cose oltre alla semplice stampa del vettore.


11

Basta copiare il contenitore sulla console.

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

Dovrebbe produrre:

1 2 3 4

8

Il problema è probabilmente nel ciclo precedente: (x = 17; isalpha(firstsquare); x++). Questo ciclo non funzionerà affatto (se firstsquarenon è alfa) o funzionerà per sempre (se è alfa). Il motivo è che firstsquarenon cambia man mano che xaumenta.


7

In C ++ 11, un loop basato su range potrebbe essere una buona soluzione:

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

Produzione:

a b c 

6

Utilizzo std::copyma senza separatore finale aggiuntivo

Un approccio alternativo / modificato usando std::copy(come originariamente usato nella risposta @JoshuaKravtiz ) ma senza includere un separatore finale aggiuntivo dopo l'ultimo 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;
}

Esempio di utilizzo applicato al contenitore di un tipo POD personalizzato:

// 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

operatore di sovraccarico <<:

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

Vedo due problemi. Come sottolineato in for (x = 17; isalpha(firstsquare); x++)c'è un ciclo infinito o mai eseguito, e anche if (entrance == 'S')se il carattere di ingresso è diverso da 'S' allora nulla viene spinto verso il vettore percorso, rendendolo vuoto e quindi non stampando nulla sullo schermo. È possibile testare quest'ultimo controllando path.empty()o stampandopath.size() .

In entrambi i casi, non sarebbe meglio usare una stringa anziché un vettore? Puoi accedere al contenuto della stringa anche come un array, cercare caratteri, estrarre sottostringhe e stampare facilmente la stringa (senza loop).

Fare tutto con le stringhe potrebbe essere il modo di scriverlo in un modo meno contorto e più facile individuare il problema.


3

Questa risposta si basa sulla risposta di Zorawar, ma non ho potuto lasciare un commento lì.

Puoi rendere const la versione auto (C ++ 11) / typedef usando cbegin e cend

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

1

In 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] << ' ';

Questa risposta non fornisce alcuna informazione aggiuntiva rispetto alle risposte già esistenti.
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
Ciao! Benvenuto in StackOverflow. Sarebbe bello se potessi includere nello snippet di codice una spiegazione di ciò che stai facendo e di come risponde alla domanda.
Slabgorb,


0

Questo ha funzionato per me:

    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

Per coloro che sono interessati: ho scritto una soluzione generalizzata che prende il meglio da entrambi i mondi, è più generalizzata a qualsiasi tipo di intervallo e inserisce virgolette attorno a tipi non aritmetici (desiderati per tipi simili a stringhe). Inoltre, questo approccio non dovrebbe avere problemi di ADL ed evitare anche "sorprese" (poiché è stato aggiunto esplicitamente caso per 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;
}

Ora è abbastanza facile da usare su qualsiasi intervallo:

std::cout << range_out{ my_vector };

Il controllo simile a una stringa lascia spazio a miglioramenti. Devo anche static_assertcontrollare la mia soluzione per evitare std::basic_string<>, ma l'ho lasciato fuori qui per semplicità.


-1

Puoi scrivere la tua funzione:

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

-1

Per le persone che desiderano una linea senza loop:

Non riesco a credere che nessuno abbia pensato a questo, ma forse è a causa dell'approccio più simile al C. Ad ogni modo, è perfettamente sicuro farlo senza un loop, in un one-liner, ASSICURANDO che std::vector<char>sia terminato con null:

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

Ma vorrei avvolgerlo nell'operatore ostream, come suggerito da @Zorawar, solo per sicurezza:

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

Possiamo ottenere un comportamento simile usando printfinvece:

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

NOTA:

L' ostreamoperatore sovraccarico deve accettare il vettore come non const. Ciò potrebbe rendere il programma insicuro o introdurre un codice non utilizzabile. Inoltre, poiché viene aggiunto il carattere null, std::vectorpotrebbe verificarsi una riallocazione del carattere . Quindi l'utilizzo di for-loop con iteratori sarà probabilmente più veloce.


1
1. fprintf(stdout, "%s\n", &test[0]);non è diverso da std::cout << test.data(), entrambi richiedono un vettore con terminazione null. 2. L'operatore "Ma lo avvolgo nell'operatore ostream" << che modifica l'operando giusto è una pessima idea.
HolyBlackCat,

Ho usato fprintf(stdout, "%s\n", &test[0]);a lungo nel codice senza che non mi abbia mai dato problemi. Interessante! E sono d'accordo che non è così bello modificare il vettore nell'operatore ostream, ma non mi piace sia il ciclo manuale che l' uso di iteratori. In qualche modo mi sento come se per semplici operazioni come la stampa su una std::vector<char>libreria standard dovessi nascondere queste cose. Ma il C ++ si sviluppa costantemente, potrebbe arrivare presto.
ワ イ き ん ぐ,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.