Bagaimana cara mencetak isi vektor?


282

Saya ingin mencetak isi vektor di C ++, inilah yang saya miliki:

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

Bagaimana cara mencetak konten vektor ke layar?


1
mengapa "tidak bekerja"?
Default

Jawaban:


394

Murni untuk menjawab pertanyaan Anda, Anda dapat menggunakan iterator:

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

Jika Anda ingin memodifikasi konten vektor dalam for for loop, maka gunakan iteratoralih-alih const_iterator.

Tapi ada banyak lagi yang bisa dikatakan tentang ini. Jika Anda hanya ingin jawaban yang dapat Anda gunakan, maka Anda bisa berhenti di sini; kalau tidak, baca terus.

otomatis (C ++ 11) / typedef

Ini bukan solusi lain, tetapi suplemen untuk iteratorsolusi di atas . Jika Anda menggunakan standar C ++ 11 (atau lebih baru), maka Anda dapat menggunakan autokata kunci untuk membantu keterbacaan:

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

Tetapi tipe iakan non-const (yaitu, kompiler akan digunakan std::vector<char>::iteratorsebagai tipe i).

Dalam hal ini, Anda mungkin hanya menggunakan a typedef(tidak terbatas pada C ++ 11, dan sangat berguna untuk digunakan):

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

melawan

Anda tentu saja dapat menggunakan tipe integer untuk merekam posisi Anda dalam forloop:

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

Jika Anda akan melakukan ini, lebih baik menggunakan tipe anggota penampung, jika tersedia dan sesuai. std::vectormemiliki tipe anggota yang dipanggil size_typeuntuk pekerjaan ini: itu adalah tipe yang dikembalikan oleh sizemetode.

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

Mengapa tidak menggunakan ini saja di atas iteratorsolusinya? Untuk kasus-kasus sederhana Anda mungkin juga, tetapi intinya adalah bahwa iteratorkelas adalah objek yang dirancang untuk melakukan pekerjaan ini untuk objek yang lebih rumit di mana solusi ini tidak ideal.

berbasis rentang untuk loop (C ++ 11)

Lihat solusi Jefffrey . Di C ++ 11 (dan lebih baru) Anda bisa menggunakan forloop berbasis rentang yang baru , yang terlihat seperti ini:

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

Karena pathmerupakan vektor item (secara eksplisit std::vector<char>), objeknya ibertipe item dari vektor (yaitu, secara eksplisit, ia bertipe char). Objek imemiliki nilai yang merupakan salinan dari item aktual di pathobjek. Dengan demikian, semua perubahan ipada loop tidak dipertahankan dengan pathsendirinya. Selain itu, jika Anda ingin menerapkan fakta bahwa Anda tidak ingin dapat mengubah nilai yang disalin idalam loop, Anda dapat memaksa tipe imenjadi const charseperti ini:

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

Jika Anda ingin mengubah item dalam path, Anda dapat menggunakan referensi:

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

dan bahkan jika Anda tidak ingin memodifikasi path, jika menyalin objek mahal, Anda harus menggunakan referensi const daripada menyalin dengan nilai:

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

std :: copy

Lihat jawaban Yosua . Anda dapat menggunakan algoritma STL std::copyuntuk menyalin konten vektor ke aliran output. Ini adalah solusi yang elegan jika Anda merasa nyaman dengan itu (dan selain itu, ini sangat berguna, tidak hanya dalam hal ini mencetak isi vektor).

std :: for_each

Lihat solusi Max . Menggunakan std::for_eachberlebihan untuk skenario sederhana ini, tetapi ini adalah solusi yang sangat berguna jika Anda ingin melakukan lebih dari sekadar mencetak ke layar: menggunakan std::for_eachmemungkinkan Anda untuk melakukan operasi (masuk akal) apa pun pada konten vektor.

overload ostream :: operator <<

Lihat jawaban Chris , ini lebih melengkapi jawaban lain karena Anda masih perlu menerapkan salah satu solusi di atas dalam kelebihan beban. Dalam contohnya dia menggunakan penghitung dalam satu forlingkaran. Sebagai contoh, ini adalah bagaimana Anda dapat dengan cepat menggunakan solusi 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;
}

Penggunaan salah satu solusi lain harus langsung.

kesimpulan

Salah satu solusi yang disajikan di sini akan berfungsi. Terserah Anda dan kode mana yang "terbaik". Apa pun yang lebih terperinci dari ini mungkin sebaiknya ditanyakan pada masalah lain di mana pro / kontra dapat dievaluasi dengan benar; tetapi seperti biasa preferensi pengguna akan selalu berperan: tidak ada solusi yang disajikan salah, tetapi beberapa akan terlihat lebih bagus untuk masing-masing pembuat kode.

tambahan

Ini adalah solusi diperluas dari yang sebelumnya saya posting. Karena posting itu terus mendapat perhatian, saya memutuskan untuk mengembangkannya dan merujuk ke solusi hebat lainnya yang diposting di sini. Posting asli saya memiliki komentar yang menyebutkan bahwa jika Anda sedang berniat pada memodifikasi vektor Anda di dalam forlingkaran maka ada dua metode yang disediakan oleh std::vectorelemen akses: std::vector::operator[]yang tidak melakukan batas memeriksa, dan std::vector::atyang tidak melakukan batas memeriksa. Dengan kata lain, atakan melempar jika Anda mencoba mengakses elemen di luar vektor dan operator[]tidak mau. Saya hanya menambahkan komentar ini, awalnya, demi menyebutkan sesuatu yang mungkin perlu diketahui jika ada orang yang tidak. Dan saya tidak melihat perbedaan sekarang. Karena itu tambahan ini.


Jika Anda mengulang 0melalui vector::size()dan vektor tidak dimodifikasi dalam loop tidak perlu menggunakan at()dan dikenakan batas tambahan memeriksa overhead. Yang mengatakan, saya akan pergi dengan iterator seperti yang Anda sarankan.
Ed S.

1
@Ed: ya, tidak ada gunanya menggunakan atjika tidak ada di memodifikasi lingkaran vektor, tapi saya pikir saya akan menyebutkannya hanya dalam kasus vektor adalah dimodifikasi dalam loop (unrecommended sebagai yang mungkin) dan karena itu tidak pernah mendapat sebutkan dan mungkin bermanfaat untuk, setidaknya, mengetahuinya.
Zorawar

Berbasis rentang untuk loop dapat ditulis ulang untuk menggunakan referensi, yang mungkin penting dalam kasus sub-objek besar, sebagai berikut:for (auto const &i: path) std::cout << i << ' ';
underscore_d

@underscore_d: terima kasih. Saya sudah membersihkan bagian itu dan saya harap itu lebih lengkap dan sedikit lebih jelas sekarang.
Zorawar

"operator kelebihan <<" bukan solusi yang baik; setidaknya satu operan dari operator yang kelebihan beban haruslah kelas yang ditentukan oleh program Anda, karena pencarian yang bergantung pada argumen
MM

218

Cara yang lebih mudah untuk melakukan ini adalah dengan algoritma penyalinan standar :

#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 adalah apa yang disebut adaptor iterator . Itu templatized atas jenis untuk dicetak ke aliran (dalam hal ini, char). cout(alias keluaran konsol) adalah aliran yang ingin kita tulis, dan karakter spasi ( " ") adalah apa yang ingin kita cetak di antara setiap elemen yang disimpan dalam vektor.

Algoritma standar ini sangat kuat dan banyak lainnya. Kekuatan dan fleksibilitas yang diberikan perpustakaan standar kepada Anda adalah apa yang membuatnya begitu hebat. Bayangkan saja: Anda dapat mencetak vektor ke konsol hanya dengan satu baris kode. Anda tidak harus berurusan dengan kasing khusus dengan karakter pemisah. Anda tidak perlu khawatir tentang for-loop. Perpustakaan standar melakukan semuanya untuk Anda.


3
bagaimana jika vektor saya adalah tipe vector<pair<int, struct node>>. Bagaimana cara menggunakan metode di atas untuk mencetak vektor ini?
mtk

6
String pembatas ditulis setelah setiap elemen, bukan antara, yaitu, juga setelah yang terakhir. Itu mungkin memerlukan berurusan dengan kasus-kasus khusus jika Anda hanya ingin antara, yaitu, sebagai pemisah.
Quigi

2
@mtk Anda dapat mendeklarasikan operator<<fungsi untuk pasangan spesifik Anda <>.
ShoeLace

Menambahkan jawaban yang menunjukkan pendekatan yang sama tetapi memperhitungkan komentar @Quigi: di atas, mengenai pemisah tambahan tambahan.
dfri

@ShoeLace Apakah tidak ada cara lain?
thegreatcoder

69

Di C ++ 11 sekarang Anda dapat menggunakan rentang berbasis untuk loop :

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

Ini berfungsi baik hanya jika ukuran vektor tidak diubah dalam tubuh kisaran untuk loop.
Brian

8
@BrianP. Ya. Mencetak elemen wadah tidak mengubah jangkauan wadah.
Sepatu

Apa yang lebih disukai di sini - c sebagai salinan nilai atau sebagai referensi const untuk menghindari penyalinan elemen?
kleinfreund

@kleinfreund Tergantung pada konten vektor. Misalnya untuk vektor chars, kemungkinan lewatnya referensi konstan sebenarnya lebih mahal daripada nilai. Tapi di sini kita berbicara tentang optimasi super mikro.
Sepatu

43

Saya pikir cara terbaik untuk melakukan ini adalah dengan kelebihan operator<<dengan menambahkan fungsi ini ke program Anda:

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

Kemudian Anda dapat menggunakan <<operator pada kemungkinan vektor, dengan asumsi elemen-elemennya juga telah ostream& operator<<ditentukan:

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;

Keluaran:

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

3
Menyimpan v.size () - 1 sebagai sebuah int adalah kemungkinan hilangnya presisi. Saya memperbaikinya dalam sunting peer review yang diterima ( stackoverflow.com/revisions/23397700/5 ), tetapi setelah itu diedit kembali memulihkan kemungkinan hilangnya presisi. Saya kira itu tidak terlalu penting dalam praktik karena vektor biasanya tidak sebesar itu.
JDiMatteo

Tidak menyimpannya sebagai variabel mengurangi pembacaan kode, yang merupakan bagian dari edit Anda saya tidak setuju. Saya telah mengubah jenis lastke size_t.
Chris Redford

size_t last = v.size() - 1;terlihat berlebihan, Anda dapat menggunakan if (i) out << ", ";kondisi sebelum out << v[i]; tautan
Vladimir Gamalyan

3
Operator ini tidak ditemukan oleh ADL, karena tidak ada dalam namespace argumennya. Jadi itu akan disembunyikan oleh namespace lainnya operator<<. Contoh
MM

Jika Anda akan melakukan ini, mengapa harus menguji if (i != last) setiap kali dalam lingkaran? Sebaliknya, jika wadah tidak kosong maka (a) kirim elemen pertama, dan kemudian (b) kirim loop- elemen yang tersisa , cetak separator terlebih dahulu (sebagai awalan). Tidak diperlukan tes loop dalam (terlepas dari kondisi loop itu sendiri). Hanya satu tes di luar lingkaran yang diperlukan.
WhozCraig

22

Bagaimana for_each+ ekspresi lambda :

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

Tentu saja, rentang berbasis untuk adalah solusi paling elegan untuk tugas konkret ini, tetapi yang satu ini memberikan banyak kemungkinan lain juga.

Penjelasan

The for_eachalgoritma mengambil berbagai masukan dan objek callable , memanggil objek ini pada setiap elemen dari jangkauan. Sebuah rentang input didefinisikan oleh dua iterator . Sebuah objek callable bisa menjadi fungsi, pointer ke fungsi, objek kelas yang overload () operatoratau seperti dalam kasus ini, ekspresi lambda . Parameter untuk ekspresi ini cocok dengan jenis elemen dari vektor.

Keindahan implementasi ini adalah kekuatan yang Anda dapatkan dari ekspresi lambda - Anda dapat menggunakan pendekatan ini untuk lebih banyak hal daripada hanya mencetak vektor.


11

Cukup salin wadah ke konsol.

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

Haruskah output:

1 2 3 4

8

Masalahnya mungkin dalam lingkaran sebelumnya: (x = 17; isalpha(firstsquare); x++). Loop ini tidak akan berjalan sama sekali (jika firstsquarenon-alpha) atau akan berjalan selamanya (jika itu alpha). Alasannya adalah itu firstsquaretidak berubah sebagaimana xbertambah.


7

Di C ++ 11, rentang berbasis untuk loop mungkin merupakan solusi yang baik:

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

Keluaran:

a b c 

6

Menggunakan std::copytetapi tanpa pemisah tambahan tambahan

Alternatif / pendekatan yang dimodifikasi menggunakan std::copy(seperti yang awalnya digunakan dalam jawaban @JoshuaKravtiz ) tetapi tanpa menyertakan tambahan pemisah tambahan setelah elemen terakhir:

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

Contoh penggunaan yang diterapkan pada wadah jenis POD khusus:

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

operator kelebihan beban <<:

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

Pemakaian:

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

3

Saya melihat dua masalah. Seperti yang ditunjukkan di for (x = 17; isalpha(firstsquare); x++)sana, apakah ada loop tak terbatas atau tidak pernah dieksekusi sama sekali, dan juga if (entrance == 'S')jika karakter pintu masuk berbeda dari 'S' maka tidak ada yang didorong ke path vektor, membuatnya kosong dan dengan demikian tidak mencetak apa pun di layar. Anda dapat menguji yang terakhir untuk memeriksa path.empty()atau mencetak path.size().

Either way, bukankah lebih baik menggunakan string daripada vektor? Anda dapat mengakses konten string seperti array juga, mencari karakter, mengekstraksi substring dan mencetak string dengan mudah (tanpa loop).

Melakukan semuanya dengan string mungkin merupakan cara untuk membuatnya ditulis dengan cara yang tidak berbelit-belit dan lebih mudah untuk menemukan masalahnya.


3

Jawaban ini didasarkan pada jawaban dari Zorawar, tetapi saya tidak dapat meninggalkan komentar di sana.

Anda dapat membuat const versi otomatis (C ++ 11) / typedef dengan menggunakan cbegin dan cend sebagai gantinya

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

1

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

Jawaban ini tidak memberikan informasi tambahan apa pun dibandingkan dengan jawaban yang sudah ada.
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
Hai! Selamat datang di stackoverflow. Akan lebih bagus jika Anda bisa memasukkan potongan kode dengan penjelasan tentang apa yang Anda lakukan, dan bagaimana itu menjawab pertanyaan.
Slabgorb


0

Ini bekerja untuk saya:

    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

Bagi mereka yang tertarik: Saya menulis solusi umum yang mengambil yang terbaik dari kedua dunia, lebih digeneralisasi untuk semua jenis rentang dan menempatkan tanda kutip di sekitar tipe non-aritmatika (diinginkan untuk tipe seperti string). Selain itu, pendekatan ini seharusnya tidak memiliki masalah ADL dan juga menghindari 'kejutan' (karena ditambahkan secara eksplisit berdasarkan kasus per kasus):

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

Sekarang cukup mudah digunakan pada rentang apa pun:

std::cout << range_out{ my_vector };

Cek seperti string menyisakan ruang untuk perbaikan. Saya juga harus static_assertmemeriksa solusi saya untuk menghindari std::basic_string<>, tetapi saya meninggalkannya di sini untuk kesederhanaan.


-1

Anda dapat menulis fungsi Anda sendiri:

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

-1

Untuk orang yang menginginkan one-liner tanpa loop:

Saya tidak percaya bahwa tidak ada seorang pun yang berpikir demikian, tetapi mungkin itu karena pendekatan yang lebih mirip C. Bagaimanapun, sangat aman untuk melakukan ini tanpa loop, dalam satu-liner, MENANGGUNG bahwa std::vector<char>null-terminated:

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

Tapi saya akan membungkus ini di ostreamoperator, seperti yang disarankan @Zorawar, agar aman:

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

Kita dapat mencapai perilaku serupa dengan menggunakan printfsebagai gantinya:

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

CATATAN:

ostreamOperator kelebihan beban harus menerima vektor sebagai non-const. Ini mungkin membuat program tidak aman atau memperkenalkan kode yang salah. Juga, karena karakter-nol ditambahkan, realokasi yang std::vectormungkin terjadi. Jadi menggunakan for-loop dengan iterator kemungkinan akan lebih cepat.


1
1. fprintf(stdout, "%s\n", &test[0]);tidak berbeda dari std::cout << test.data(), keduanya membutuhkan vektor yang diakhiri dengan nol. 2. "Tapi saya akan membungkus ini di operator ostream" << operator yang memodifikasi operan yang tepat adalah ide yang sangat buruk.
HolyBlackCat

Saya sudah menggunakan fprintf(stdout, "%s\n", &test[0]); kode dalam waktu lama tanpa pernah memberi saya masalah. Menarik! Dan saya setuju bahwa tidak begitu baik untuk memodifikasi vektor di ostreamoperator, tapi saya tidak suka perulangan secara manual dan menggunakan iterator. Entah bagaimana saya merasa untuk operasi sederhana seperti mencetak std::vector<char>perpustakaan standar harus menyembunyikan hal-hal ini. Tapi C ++ terus berkembang, mungkin akan segera hadir.
ワ イ き ん ぐ
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.