it-swarm.it

Vieni a stampare il contenuto di un vettore?

Voglio stampare il contenuto di un vettore in C++, ecco quello che 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?

230
forthewinwin

Puramente 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 vuoi modificare il contenuto del vettore nel ciclo for, usa iterator piuttosto che const_iterator.

Ma c'è molto di più che si può dire su questo. Se vuoi solo una risposta che puoi usare, allora puoi fermarti qui; altrimenti, continua a leggere.

auto (C++ 11)/typedef

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

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

Ma il tipo di i sarà non-const (cioè, il compilatore userà std::vector<char>::iterator come tipo di i).

In questo caso, potresti anche usare un typedef (non limitato a C++ 11 e very 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 ciclo for:

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

Se hai intenzione di fare questo, è meglio usare i tipi di membri del contenitore, se sono disponibili e appropriati. std::vector ha un tipo di membro chiamato size_type per questo lavoro: è il tipo restituito dal metodo size.

// 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 soluzione iterator? Per i casi più semplici si potrebbe anche, ma il punto è che la classe iterator è un oggetto progettato per fare questo lavoro per oggetti più complicati in cui questa soluzione non sarà ideale.

range-based for loop (C++ 11)

Vedi La soluzione di Jefffrey . In C++ 11 (e successivi) è possibile utilizzare il nuovo ciclo for basato sulla gamma, 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'elemento del vettore (ad esempio, in modo esplicito, è di tipo char). L'oggetto i ha un valore che è una copia dell'oggetto reale nell'oggetto path. Pertanto, tutte le modifiche a i nel ciclo non vengono conservate in path stesso. Inoltre, se vuoi far rispettare il fatto che non vuoi essere in grado di cambiare il valore copiato di i nel ciclo, puoi forzare il tipo di i ad essere const char in questo 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 si desidera modificare path, se la copia di oggetti è costosa si dovrebbe 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::copy per copiare i contenuti vettoriali sul flusso di output. Questa è una soluzione elegante se ci si sente a proprio agio (e inoltre, è very useful, non solo in questo caso della stampa del contenuto di un vettore).

std :: for_each

Vedi La soluzione di Max . Usare std::for_each è eccessivo per questo semplice scenario, ma è una soluzione molto utile se si desidera fare qualcosa di più della semplice stampa sullo schermo: l'utilizzo di std::for_each consente di eseguire un'operazione any (sensible) sui contenuti vettoriali.

overload ostream :: operator <<

Vedi La risposta di Chris , questo è più un complemento alle altre risposte poiché dovrai ancora implementare una delle soluzioni sopra riportate nell'overloading. Nel suo esempio ha usato un contatore in un ciclo for. Ad esempio, questo è il modo in cui puoi rapidamente usare 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'utilizzo di una qualsiasi delle altre soluzioni dovrebbe essere semplice.

conclusione

Qualunque delle soluzioni presentate qui funzionerà. Dipende da te e dal codice su cui si è il "migliore". Qualunque cosa più dettagliata di questa è probabilmente la migliore per un'altra domanda dove i pro/contro possono essere adeguatamente valutati; ma come sempre le preferenze degli utenti giocheranno sempre una parte: nessuna delle soluzioni presentate è sbagliata, ma alcune appariranno più gradevoli a ogni singolo codificatore.

appendice

Questa è una soluzione ampliata di una versione precedente che ho pubblicato. Dal momento che quel post ha continuato ad attirare l'attenzione, ho deciso di espanderci e fare riferimento alle altre eccellenti soluzioni che sono state pubblicate qui. Il mio post originale aveva un'osservazione che diceva che se were intendeva modificare il proprio vettore all'interno di un ciclo for allora ci sono due metodi forniti da std::vector per accedere agli elementi: std::vector::operator[] che non esegue il controllo dei limiti e std::vector::at che esegue il controllo dei limiti. In altre parole, at genererà se si tenta di accedere a un elemento esterno al vettore e operator[] non lo farebbe. Ho solo aggiunto questo commento, in origine, per menzionare qualcosa che potrebbe essere utile sapere se qualcuno non l'avesse già fatto. E non vedo alcuna differenza ora. Quindi questo addendum.

321
Zorawar

Un modo molto più semplice per farlo è con l'algoritmo copy 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 è ciò che viene chiamato adattatore iteratore. È templatizzato sul tipo da stampare sullo stream (in questo caso, char). cout (alias output della console) è lo stream su cui vogliamo scrivere, e il carattere dello 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à offerte dalla libreria standard sono ciò che lo rende così eccezionale. Immagina: puoi stampare un vettore sulla console con just one line of code. Non devi occuparti di casi speciali con il carattere separatore. Non devi preoccuparti di for-loops. La libreria standard fa tutto per te.

197
Joshua Kravitz

In C++ 11 ora puoi usare un range-based per loop :

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

Penso che il modo migliore per farlo sia quello di 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 puoi usare l'operatore << su qualsiasi vettore possibile, assumendo che i suoi elementi abbiano anche 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}
37
Chris Redford

Che ne dici difor_each + espressione lambda :

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

Ovviamente, un range-based per è la soluzione più elegante per questo compito concreto, ma anche questo offre molte altre possibilità.

Spiegazione

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

La bellezza di questa implementazione è il potere che si ottiene dalle espressioni lambda: è possibile utilizzare questo approccio per molte più cose rispetto alla semplice stampa del vettore.

18
Maxim Chetrusca

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 uscita:

1 2 3 4
8
g24l

Il problema è probabilmente nel ciclo precedente: (x = 17; isalpha(firstsquare); x++). Questo ciclo non funzionerà affatto (se firstsquare è non-alpha) o verrà eseguito per sempre (se è alpha). Il motivo è che firstsquare non cambia in quanto x viene incrementato.

8
MSalters

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

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

Produzione:

a b c 
5

Usando std::copy ma senza extra trailing separator

Un approccio alternativo/modificato usando std::copy (come originariamente usato in @JoshuaKravtiz answer ) 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;
}
3
dfri

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
ivanmara

Vedo due problemi. Come sottolineato in for (x = 17; isalpha(firstsquare); x++), c'è un ciclo infinito o non viene mai eseguito affatto, e anche in if (entrance == 'S') se il carattere di ingresso è diverso da 'S', quindi nulla viene premuto sul vettore percorso, rendendolo vuoto e quindi non stampa nulla sullo schermo. Puoi testare quest'ultimo controllo per path.empty() o stampare path.size().

In ogni caso, non sarebbe meglio usare una stringa invece di un vettore? È possibile accedere ai contenuti della stringa come un array, cercare caratteri, estrarre sottostringhe e stampare facilmente la stringa (senza un loop).

Fare tutto con le stringhe potrebbe essere il modo per farlo scrivere in un modo meno complesso e più facile da individuare il problema.

3
miguelbernadi

Questa risposta è basata sulla risposta di Zorawar, ma non potevo lasciare un commento lì.

Puoi fare la versione auto (C++ 11)/typedef versione cbegin e cend invece

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

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

Puoi scrivere la tua funzione:

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