Jak wydrukować zawartość wektora?


282

Chcę wydrukować zawartość wektora w C ++, oto co mam:

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

Jak wydrukować zawartość wektora na ekranie?


1
dlaczego to „nie działa”?
Domyślnie

Odpowiedzi:


394

Aby odpowiedzieć na twoje pytanie, możesz użyć iteratora:

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

Jeśli chcesz zmodyfikować zawartość wektora w pętli for, użyj iteratorraczej niż const_iterator.

Ale jest o wiele więcej do powiedzenia na ten temat. Jeśli chcesz tylko odpowiedzi, której możesz użyć, możesz zatrzymać się tutaj; w przeciwnym razie czytaj dalej.

auto (C ++ 11) / typedef

To nie jest kolejne rozwiązanie, ale uzupełnienie powyższego iteratorrozwiązania. Jeśli używasz standardu C ++ 11 (lub nowszego), możesz użyć autosłowa kluczowego, aby poprawić czytelność:

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

Ale typem ibędzie non-const (tzn. Kompilator użyje std::vector<char>::iteratorjako typu i).

W takim przypadku możesz równie dobrze użyć typedef(nie ograniczonego do C ++ 11 i bardzo przydatnego w każdym razie):

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

licznik

Możesz oczywiście użyć typu liczby całkowitej, aby zapisać swoją pozycję w forpętli:

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

Jeśli masz zamiar to zrobić, lepiej użyć typów członków kontenera, jeśli są one dostępne i odpowiednie. std::vectorma typ członka wywołany size_typedla tego zadania: jest to typ zwracany przez sizemetodę.

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

Dlaczego nie wykorzystać tego zamiast iteratorrozwiązania? W prostych przypadkach równie dobrze możesz, ale chodzi o to, że iteratorklasa jest obiektem zaprojektowanym do wykonywania tego zadania w przypadku bardziej skomplikowanych obiektów, w których to rozwiązanie nie będzie idealne.

oparte na zakresie dla pętli (C ++ 11)

Zobacz rozwiązanie Jefffreya . W C ++ 11 (i nowszych) możesz użyć nowej forpętli opartej na zakresie , która wygląda następująco:

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

Ponieważ pathjest wektorem elementów (jawnie std::vector<char>), obiekt ijest typu elementu wektora (tj. Jawnie jest typu char). Obiekt ima wartość będącą kopią faktycznego elementu w pathobiekcie. Dlatego wszystkie zmiany iw pętli nie są zachowane pathsame w sobie. Dodatkowo, jeśli chcesz, aby wymusić na fakt, że nie chcesz, aby móc zmienić skopiowane wartości iw pętli, można wymusić typ ibyć const chartak:

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

Jeśli chcesz zmodyfikować elementy path, możesz użyć odwołania:

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

a nawet jeśli nie chcesz modyfikować path, jeśli kopiowanie obiektów jest drogie, powinieneś użyć stałego odwołania zamiast kopiowania według wartości:

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

std :: kopia

Zobacz odpowiedź Jozuego . Za pomocą algorytmu STL std::copymożna skopiować zawartość wektora do strumienia wyjściowego. Jest to eleganckie rozwiązanie, jeśli czujesz się z nim komfortowo (a poza tym jest bardzo przydatne, nie tylko w tym przypadku drukowania zawartości wektora).

std :: for_each

Zobacz rozwiązanie Maxa . Używanie std::for_eachjest przesadą w tym prostym scenariuszu, ale jest to bardzo przydatne rozwiązanie, jeśli chcesz zrobić więcej niż tylko drukowanie na ekranie: użycie std::for_eachpozwala na wykonanie dowolnej (sensownej) operacji na zawartości wektorowej.

przeciążenie ostream :: operator <<

Zobacz odpowiedź Chrisa , jest to bardziej uzupełnienie innych odpowiedzi, ponieważ nadal będziesz musiał wdrożyć jedno z powyższych rozwiązań w przypadku przeciążenia. W swoim przykładzie użył licznika w forpętli. Na przykład w ten sposób możesz szybko użyć rozwiązania Jozuego :

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

Korzystanie z innych rozwiązań powinno być proste.

wniosek

Każde z przedstawionych tutaj rozwiązań będzie działać. To zależy od Ciebie i od tego, który kod jest „najlepszy”. Wszystko, co jest bardziej szczegółowe niż to, jest prawdopodobnie najlepiej pozostawić w innym pytaniu, w którym zalety / wady można odpowiednio ocenić; ale jak zawsze preferencje użytkownika zawsze będą odgrywać rolę: żadne z przedstawionych rozwiązań nie jest złe, ale niektóre będą ładniejsze dla każdego kodera.

uzupełnienie

To jest rozwinięte rozwiązanie wcześniejszego, które opublikowałem. Ponieważ ten post wciąż cieszył się zainteresowaniem, postanowiłem go rozwinąć i odnieść się do innych doskonałych rozwiązań, które zostały tutaj opublikowane. Mój oryginalny post miał uwagę, że wspomniany, że jeśli były zamierzający na zmodyfikowanie wektor wewnątrz forpętli następnie istnieją dwie metody przewidziane std::vectordo elementów dostępu: std::vector::operator[]który nie robi granice kontroli, a std::vector::atktóry ma wykonać sprawdzanie ograniczeń. Innymi słowy, atwyrzuci, jeśli spróbujesz uzyskać dostęp do elementu poza wektorem i operator[]nie zrobi tego. Pierwotnie dodałem ten komentarz tylko po to, by wspomnieć o czymś, co może być przydatne, jeśli ktoś już tego nie zrobił. I nie widzę teraz żadnej różnicy. Stąd to uzupełnienie.


Jeśli pętla 0przechodzi przez vector::size()i wektor nie jest modyfikowany w pętli, nie ma potrzeby używania at()i ponoszenia dodatkowych ograniczeń sprawdzania narzutu. To powiedziawszy, wybrałbym iterator, jak sugerujesz.
Ed S.

1
@Ed: tak, nie ma sensu używać, atjeśli nic w pętli nie modyfikuje wektora, ale pomyślałem, że wspomnę o tym na wypadek, gdyby wektor został zmodyfikowany w pętli (może to być niezalecane) i ponieważ nigdy nie dostaje wspomnieć i może być przydatna, przynajmniej o tym wiedzieć.
Zorawar

Pętla oparta na zakresie może zostać przepisana w celu użycia odniesień, które mogą być ważne w przypadku dużych podobiektów, w następujący sposób:for (auto const &i: path) std::cout << i << ' ';
underscore_d

@underscore_d: dzięki. Oczyściłem tę sekcję i mam nadzieję, że jest ona teraz bardziej kompletna i nieco jaśniejsza.
Zorawar

„operator przeciążenia <<” nie jest dobrym rozwiązaniem; przynajmniej jeden operand przeciążonego operatora powinien być klasą zdefiniowaną przez twój program, z powodu wyszukiwania zależnego od argumentów
MM

218

Znacznie łatwiejszym sposobem na to jest standardowy algorytm kopiowania :

#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 to tak zwany adapter iteratora . Jest on wzorowany na typie, aby wydrukować do strumienia (w tym przypadku, char). cout(aka wyjście konsoli) to strumień, do którego chcemy pisać, a znak spacji ( " ") jest tym, co chcemy wydrukować między każdym elementem przechowywanym w wektorze.

Ten standardowy algorytm jest potężny, podobnie jak wiele innych. Moc i elastyczność, jaką zapewnia standardowa biblioteka, sprawiają, że jest ona tak wspaniała. Wyobraź sobie: możesz wydrukować wektor na konsoli za pomocą tylko jednego wiersza kodu. Nie musisz zajmować się specjalnymi przypadkami ze znakiem separatora. Nie musisz się martwić o pętle for. Standardowa biblioteka robi to wszystko za Ciebie.


3
co jeśli mój wektor był typu vector<pair<int, struct node>>. Jak użyć powyższej metody, aby wydrukować ten wektor?
mtk

6
Ciąg separatora jest zapisywany po każdym elemencie, a nie między, tzn. Także po ostatnim. Może to wymagać zajmowania się szczególnymi przypadkami, jeśli chcesz tylko między, tj. Jako separator.
Quigi,

2
@mtk możesz zadeklarować operator<<funkcję dla określonej pary <>.
ShoeLace

Dodano odpowiedź pokazującą podobne podejście, ale uwzględniającą powyższy komentarz @Quigi: dotyczący dodatkowego separatora końcowego.
dfri

@ShoeLace Czy nie ma innej możliwości?
thegreatcoder

69

W C ++ 11 możesz teraz używać pętli bazującej na zakresie :

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

Działa to świetnie tylko wtedy, gdy rozmiar wektora nie zostanie zmieniony w treści zakresu pętli.
Brian

8
@BrianP. Tak. Drukowanie elementów pojemnika nie zmienia zasięgu pojemnika.
But

Co jest tutaj preferowane - c jako kopia wartości lub stałe odwołanie, aby uniknąć kopiowania elementu?
kleinfreund

@kleinfreund Zależy to od zawartości wektora. Na przykład dla wektora chars istnieje prawdopodobieństwo, że przejście przez stałe odniesienie jest w rzeczywistości bardziej kosztowne niż wartość. Ale tutaj mówimy o super mikrooptymalizacjach.
But

43

Myślę, że najlepszym sposobem na to jest po prostu przeciążenie operator<<, dodając tę ​​funkcję do swojego programu:

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

Następnie możesz użyć <<operatora na dowolnym możliwym wektorze, zakładając, że jego elementy również mają ostream& operator<<zdefiniowane:

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;

Wyjścia:

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

3
Przechowywanie v.size () - 1 jako int jest możliwą utratą precyzji. Naprawiłem to w zaakceptowanej recenzji recenzowanej ( stackoverflow.com/revisions/23397700/5 ), ale potem ponownie ją edytowałem, przywracając możliwą utratę precyzji. Myślę, że w praktyce nie ma to większego znaczenia, ponieważ wektory zwykle nie są tak duże.
JDiMatteo

Nie zapisanie go jako zmiennej zmniejsza czytelność kodu, który jest częścią twojej edycji, z którą się nie zgadzam. Zmieniłem typ lastna size_t.
Chris Redford

size_t last = v.size() - 1;wygląda na zbędny, możesz użyć if (i) out << ", ";warunku przed out << v[i]; linkiem
Vladimir Gamalyan

3
Ten operator nie został znaleziony przez ADL, ponieważ nie znajduje się w przestrzeni nazw żadnego z jego argumentów. Będzie więc ukryty przez dowolną inną przestrzeń nazw operator<<. Przykład
MM

Jeśli masz zamiar to zrobić, po if (i != last) co testować za każdym razem w pętli? Zamiast tego, jeśli pojemnik nie jest pusty, to (a) wyślij pierwszy element, a następnie (b) wyślij pętlę pozostałe elementy, drukując najpierw separator (jako przedrostek). Nie jest wymagany test pętli wewnętrznej (oprócz samego stanu pętli). Wymagany jest tylko jeden test poza pętlą.
WhozCraig

22

Co powiesz na for_eachwyrażenie + lambda :

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

Oczywiście oparte na zakresie jest najbardziej eleganckim rozwiązaniem dla tego konkretnego zadania, ale to daje także wiele innych możliwości.

Wyjaśnienie

for_eachAlgorytm bierze zakres wejściowy i wpłacone obiekt , nazywając ten obiekt na każdym elemencie zakresie. Zakres wejściowy jest utworzony przez dwie iteratorami . Obiekt wywoływalny może być funkcją, wskaźnikiem funkcji, obiektem klasy, która się przeciąża () operatorlub, jak w tym przypadku, wyrażeniem lambda . Parametr tego wyrażenia odpowiada typowi elementów z wektora.

Piękno tej implementacji polega na sile, jaką czerpiesz z wyrażeń lambda - możesz użyć tego podejścia do znacznie więcej rzeczy niż tylko do drukowania wektora.


11

Po prostu skopiuj kontener do konsoli.

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

Powinien generować:

1 2 3 4

8

Problem jest prawdopodobnie w poprzedniej pętli: (x = 17; isalpha(firstsquare); x++). Ta pętla w ogóle nie będzie działać (jeśli firstsquarenie jest alfa) lub będzie działać wiecznie (jeśli jest alfa). Powodem jest to, że firstsquarenie zmienia się, ponieważ xjest zwiększana.


7

W C ++ 11 dobrym rozwiązaniem może być oparte na zakresie pętla:

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

Wynik:

a b c 

6

Używanie, std::copyale bez dodatkowego separatora końcowego

Alternatywne / zmodyfikowane podejście wykorzystujące std::copy(jak pierwotnie użyto w odpowiedzi @JoshuaKravtiz ), ale bez dodatkowego separatora końcowego po ostatnim elemencie:

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

Przykładowe użycie zastosowane do kontenera niestandardowego typu POD:

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

przeciążenie operatora <<:

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

Stosowanie:

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

3

Widzę dwa problemy. Jak wskazano w, for (x = 17; isalpha(firstsquare); x++)istnieje albo nieskończona pętla, albo nigdy w ogóle nie jest wykonywana, a także if (entrance == 'S')jeśli znak wejściowy jest inny niż „S”, wówczas nic nie jest wypychane do wektora ścieżki, co czyni go pustym, a tym samym nie drukuje niczego na ekranie. Możesz przetestować to drugie, sprawdzając path.empty()lub drukując path.size().

Tak czy inaczej, czy nie lepiej byłoby użyć łańcucha zamiast wektora? Możesz uzyskać dostęp do zawartości łańcucha, jak również tablicy, wyszukiwać znaki, wyodrębniać podciągi i łatwo drukować ciąg (bez pętli).

Wykonanie tego wszystkiego za pomocą łańcuchów może być sposobem na napisanie go w mniej skomplikowany sposób i łatwiejsze dostrzeżenie problemu.


3

Ta odpowiedź oparta jest na odpowiedzi Zorawara, ale nie mogłem tam zostawić komentarza.

Możesz ustawić wersję automatyczną (C ++ 11) / typedef const za pomocą cbegin i cend zamiast tego

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

1

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

Ta odpowiedź nie zawiera żadnych dodatkowych informacji w porównaniu do już istniejących odpowiedzi.
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
Cześć! Witamy w Stackoverflow. Byłoby wspaniale, gdybyś mógł dołączyć do fragmentu kodu jakieś wyjaśnienie tego, co robisz i jak odpowiada na pytanie.
Slabgorb


0

To działało dla mnie:

    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

Dla tych, którzy są zainteresowani: napisałem uogólnione rozwiązanie, które bierze to, co najlepsze z obu światów, jest bardziej uogólnione na dowolny typ zakresu i umieszcza cudzysłowy wokół typów nie arytmetycznych (pożądane dla typów łańcuchowych). Co więcej, to podejście nie powinno mieć żadnych problemów z ADL, a także unikać „niespodzianek” (ponieważ jest dodawane wyraźnie w poszczególnych przypadkach):

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

Teraz jest dość łatwy w użyciu w dowolnym zakresie:

std::cout << range_out{ my_vector };

Sznurkowy czek pozostawia pole do poprawy. Mam również static_assertrozwiązanie w celu uniknięcia std::basic_string<>, ale zostawiłem to tutaj dla uproszczenia.


-1

Możesz napisać własną funkcję:

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

-1

Dla osób, które chcą jednowarstwowe bez pętli:

Nie mogę uwierzyć, że nikt o tym nie pomyślał, ale być może wynika to z bardziej podobnego do C podejścia. W każdym razie jest to całkowicie bezpieczne, bez pętli, w jednej linijce , PRZYJMUJĄC, że std::vector<char>jest ona zakończona zerem:

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

Ale chciałbym owinąć to w ostreamoperatora, jak sugerował @Zorawar, dla bezpieczeństwa:

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

Podobne zachowanie możemy osiągnąć, stosując w printfzamian:

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

UWAGA:

Przeciążony ostreamoperator musi zaakceptować wektor jako non-const. Może to spowodować, że program będzie niepewny lub wprowadzi błędny kod. Ponadto, ponieważ dodawany jest znak zerowy, std::vectormoże nastąpić realokacja . Używanie pętli for z iteratorami będzie prawdopodobnie szybsze.


1
1. fprintf(stdout, "%s\n", &test[0]);nie różni się od std::cout << test.data(), oba wymagają wektora zakończonego zerem. 2. „Ale owinąłbym to w operator ostream” << , który modyfikuje odpowiedni operand, to bardzo zły pomysł.
HolyBlackCat

Używałem fprintf(stdout, "%s\n", &test[0]);od dłuższego czasu w kodzie, nie sprawiając mi żadnych problemów. Ciekawy! I zgadzam się, że modyfikowanie wektora w ostreamoperatorze nie jest tak miłe , ale nie podoba mi się zarówno ręczne zapętlanie, jak i używanie iteratorów. Jakoś mam wrażenie, że proste operacje, takie jak wydruk std::vector<char>standardowej biblioteki, powinny ukryć te rzeczy. Ale C ++ stale się rozwija, może wkrótce.
ワ イ き ん ぐ
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.