Bir vektörün içeriği nasıl yazdırılır?


282

C ++ bir vektör içeriğini yazdırmak istiyorum, İşte ne var:

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

Vektörün içeriğini ekrana nasıl yazdırabilirim?


1
neden "çalışmıyor"?
Varsayılan

Yanıtlar:


394

Sadece sorunuzu cevaplamak için bir yineleyici kullanabilirsiniz:

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

For döngüsünde vektörün içeriğini değiştirmek isterseniz, iteratoryerine kullanın const_iterator.

Ama bunun hakkında söylenebilecek çok şey var. Sadece kullanabileceğiniz bir cevap istiyorsanız, burada durabilirsiniz; aksi halde okumaya devam edin.

otomatik (C ++ 11) / typedef

Bu başka bir çözüm değil, yukarıdaki iteratorçözümün bir tamamlayıcısıdır . C ++ 11 standardını (veya daha yenisini) kullanıyorsanız auto, okunabilirliğe yardımcı olması için anahtar kelimeyi kullanabilirsiniz :

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

Ancak türü isabit değildir (yani, derleyici std::vector<char>::iteratortürü olarak kullanır i).

Bu durumda, sadece bir typedef(C ++ 11 ile sınırlı değil ve yine de kullanmak için çok yararlı) kullanabilirsiniz:

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

sayaç

Elbette, fordöngüdeki konumunuzu kaydetmek için bir tamsayı türü kullanabilirsiniz :

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

Bunu yapacaksanız, mevcut ve uygunsa, kabın üye türlerini kullanmak daha iyidir. bu iş için std::vectorçağrılan bir üye türüne sahiptir size_type: sizeyöntem tarafından döndürülen türdür .

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

Neden bunu sadece iteratorçözüm üzerinde kullanmıyorsunuz ? Basit durumlar için de yapabilirsiniz, ancak nokta, iteratorsınıfın, bu çözümün ideal olmayacağı daha karmaşık nesneler için bu işi yapmak için tasarlanmış bir nesne olmasıdır.

döngü için aralık tabanlı (C ++ 11)

Jefffrey'in çözümüne bakın . C ++ 11'de (ve üstü) for, aşağıdaki gibi görünen yeni aralık tabanlı döngüyü kullanabilirsiniz :

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

Yana pathöğelerin (açıkça bir vektör std::vector<char>), nesne ivektör öğenin türüne ait olduğu (yani, açıkça, bu tiptedir char). Nesnenin i, nesnedeki gerçek öğenin bir kopyası olan bir değeri vardır path. Böylece, idöngüdeki tüm değişiklikler kendi içinde korunmaz path. Ayrıca, idöngüdeki kopyalanan değerini değiştirmek istemediğiniz gerçeğini uygulamak istiyorsanız , türünü işu şekilde olmaya zorlayabilirsiniz const char:

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

İçindeki öğeleri değiştirmek pathisterseniz, bir referans kullanabilirsiniz:

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

ve değiştirmek istemeseniz bile path, nesnelerin kopyalanması pahalıysa, değere göre kopyalamak yerine bir const referansı kullanmalısınız:

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

std :: kopya

Joshua'nın cevabına bakın . std::copyVektör içeriklerini çıkış akışına kopyalamak için STL algoritmasını kullanabilirsiniz . Bu, rahat ediyorsanız zarif bir çözümdür (ve ayrıca, sadece bir vektörün içeriğini yazdırmak için değil , çok yararlıdır).

std :: for_each

Max'in çözümüne bakın . Kullanmak std::for_eachbu basit senaryo için aşırıya kaçmaktır, ancak sadece ekrana yazdırmaktan daha fazlasını yapmak istiyorsanız çok yararlı bir çözümdür: using , vektör içerikleri üzerinde herhangi bir (mantıklı) işlem std::for_eachyapmanızı sağlar .

aşırı ostream :: operatör <<

Chris'in cevabına bakın , bu aşırı yükte yukarıdaki çözümlerden birini uygulamanız gerekeceğinden, diğer cevapların bir tamamlayıcısıdır. Örneğinde bir fordöngü içinde bir sayaç kullandı . Örneğin, Joshua'nın çözümünü bu şekilde hızlı bir şekilde kullanabilirsiniz :

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

Diğer çözümlerden herhangi birinin kullanımı basit olmalıdır.

Sonuç

Burada sunulan çözümlerden herhangi biri işe yarayacaktır. Size ve "en iyi" koduna bağlıdır. Bundan daha ayrıntılı herhangi bir şey muhtemelen artılarının / eksilerin uygun şekilde değerlendirilebileceği başka bir soru için bırakılır; ancak her zamanki gibi kullanıcı tercihi her zaman bir rol oynayacaktır: sunulan çözümlerin hiçbiri yanlış değildir, ancak bazıları her bir kodlayıcı için daha hoş görünecektir.

ek

Bu, daha önce yayınladığımın genişletilmiş bir çözümüdür. Bu yazı dikkat çekmeye devam ettiğinden, onu genişletmeye ve burada yayınlanan diğer mükemmel çözümlere başvurmaya karar verdim. Benim orijinal yayın eğer bu konuda bir açıklama vardı edilmiştir bir iç vektör değiştirerek müstakbel forsonra orada tarafından sağlanan iki yöntem vardır döngü std::vector: Erişim elemanlarına std::vector::operator[]sınırları denetleme yapmaz ve std::vector::atbağlantıda bulunduğu sınırları denetleme gerçekleştirin. Diğer bir deyişle, atvektörün dışındaki bir öğeye erişmeye çalışırsanız ve atmazsanız atar operator[]. Bu yorumu sadece, daha önce kimsenin yapmadığını bilmek yararlı olabilecek bir şeyden bahsetmek için ekledim. Ve şimdi hiçbir fark görmüyorum. Dolayısıyla bu zeyilname.


Eğer 0içinden döngü yapıyorsanız vector::size()ve vektör döngü içinde değiştirilmediyse at(), ek kontrol sınırlarını kontrol etmeye ve kullanmanıza gerek yoktur . Bununla birlikte, önerdiğiniz gibi bir yineleyici ile giderdim.
Ed S.

1
@Ed: evet, orada kullanarak hiçbir nokta atdöngü tuşelere hiçbir şey vektörü, ama düşündüm ben vektör ihtimale o söz ediyorum eğer edilir (yani olabilir olarak unrecommended) döngü içinde değiştirilebilir ve bu çünkü geçmez ve en azından bunu bilmek faydalı olabilir.
Zorawar

Döngü tabanlı aralık, aşağıdaki gibi büyük alt nesneler için önemli olabilecek referansları kullanmak için yeniden yazılabilir:for (auto const &i: path) std::cout << i << ' ';
underscore_d

@underscore_d: teşekkürler. Bu bölümü temizledim ve umarım şimdi hem daha eksiksiz hem de biraz daha açıktır.
Zorawar

"aşırı yük operatörü <<" iyi bir çözüm değildir; aşırı yüklenmiş operatörün en az bir işleneni, bağımsız değişkene bağlı arama nedeniyle programınız tarafından tanımlanan bir sınıf olmalıdır
MM

218

Bunu yapmanın çok daha kolay bir yolu standart kopya algoritmasıdır :

#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, yineleyici bağdaştırıcısıdır . Akıma yazdırmak için türün üzerinde ayarlanmıştır (bu durumda, char). cout(diğer adıyla konsol çıktısı) yazmak istediğimiz akıştır ve boşluk karakteri ( " ") vektörde depolanan her öğe arasında yazdırılmasını istediğimiz alandır .

Bu standart algoritma güçlüdür ve diğerleri de öyle. Standart kütüphanenin size sağladığı güç ve esneklik, onu bu kadar harika yapan şeydir. Düşünün: sadece bir kod satırı ile konsola bir vektör yazdırabilirsiniz . Ayırıcı karakterli özel durumlar ile uğraşmak zorunda değilsiniz. Döngüler için endişelenmenize gerek yok. Standart kütüphane hepsini sizin için yapar.


3
ya benim vektörüm türdeyse vector<pair<int, struct node>>. Bu vektörü yazdırmak için yukarıdaki yöntemi nasıl kullanabilirim?
mtk

6
Sınırlayıcı dize, her öğeden sonra yazılır , aralarında değil, yani son öğeden sonra da yazılır . Bu, yalnızca ayırıcı olarak istiyorsanız, özel durumlar ile uğraşmayı gerektirebilir.
Quigi

2
@mtk operator<<, özel çiftiniz için bir fonksiyon beyan edebilirsiniz <>.
ShoeLace

Benzer bir yaklaşımı gösteren ancak ekstra takip eden ayırıcıya ilişkin yukarıdaki yukarıdaki @Quigi: s yorumunu içeren bir cevap eklendi .
dfri

@ShoeLace Başka yolu yok mu?
thegreatcoder

69

C ++ 11'de artık döngü için aralık tabanlı kullanabilirsiniz :

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

Bu, yalnızca vektörün boyutu döngü aralığının gövdesinde değiştirilmezse harika çalışır.
Brian

8
@BrianP. Evet. Bir kabın elemanlarını yazdırmak kabın aralığını değiştirmez.
Ayakkabı

Burada tercih edilen nedir - c öğenin kopyalanmasını önlemek için bir değer kopyası veya sabit bir başvuru olarak?
kleinfreund

@kleinfreund Vektörün içeriğine bağlıdır. Örneğin, bir chars vektörü için , sabit referansla geçişin aslında değere göre daha maliyetli olmasıdır. Ancak burada süper mikro optimizasyonlardan bahsediyoruz.
Ayakkabı

43

Bunu yapmanın en iyi yolunun operator<<, programınıza bu işlevi ekleyerek aşırı yükleme yapmak olduğunu düşünüyorum :

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

Ardından <<, öğelerinin de ostream& operator<<tanımladığı varsayılarak , operatörü olası herhangi bir vektörde kullanabilirsiniz :

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;

Çıktılar:

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

3
V.size () - 1 öğesini int olarak saklamak, olası bir hassasiyet kaybıdır. Bunu kabul edilen bir hakemli derleme düzenlemesinde ( stackoverflow.com/revisions/23397700/5 ) düzelttim , ancak daha sonra olası hassasiyet kaybını geri yükleyerek yeniden düzenlendi. Vektörler genellikle o kadar büyük olmadığı için pratikte çok önemli değil sanırım.
JDiMatteo

Değişken olarak saklamamanız, katılmadığınız düzenlemenizin bir parçası olan kodun okunabilirliğini azaltır. Ben türünü değiştirdik lastiçin size_t.
Chris Redford

size_t last = v.size() - 1;gereksiz görünüyor, bağlantıdanif (i) out << ", "; önce durumu kullanabilirsinizout << v[i];
Vladimir Gamalyan

3
Bu işleç, bağımsız değişkenlerinin hiçbirinin ad alanında olmadığı için ADL tarafından bulunamadı. Böylece diğer ad alanları tarafından gizlenecektir operator<<. Örnek
MM

Bunu yapmak gidiyoruz, neden sınamak if (i != last) her zaman içinde döngü? Bunun yerine, kap boş değilse (a) ilk elemanı gönderir ve sonra (b) kalan elemanları döngüye gönderir, önce ayırıcıyı basar (ön ek olarak). Hiçbir iç döngü testi (döngü koşulunun kendisinin dışında) gerekli değildir. Sadece bir devre dışı test gereklidir.
WhozCraig

22

for_each+ Lambda ifadesine ne dersiniz :

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

Tabii ki, menzil tabanlı bu somut görev için en zarif çözümdür, ancak bu da birçok olasılık sunar.

açıklama

for_eachAlgoritma, bir alan giriş aralığı ve çağrılabilir nesne aralığının her eleman bu nesneyi arama. Bir giriş aralığı iki yineleyici tarafından tanımlanır . Bir çağrılabilir bir amacı , bir işlev, fonksiyon bir gösterici, burada aşırı yükler, bir sınıf nesnesi olabilir () operatorveya bu durumda olduğu gibi , bir lambda ifadesi . Bu ifadenin parametresi, vektördeki öğelerin türüyle eşleşir.

Bu uygulamanın güzelliği lambda ifadelerinden elde ettiğiniz güçtür - bu yaklaşımı sadece vektörü yazdırmaktan çok daha fazla şey için kullanabilirsiniz.


11

Konteyneri konsola kopyalamanız yeterlidir.

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

Çıktı gerekir:

1 2 3 4

8

Sorun önceki döngüde muhtemelen: (x = 17; isalpha(firstsquare); x++). Bu döngü hiç ( firstsquarealfa değilse ) veya sonsuza kadar (alfa ise) çalışır. Bunun nedeni , arttıkça firstsquaredeğişmemesidir x.


7

C ++ 11'de, döngü için aralık tabanlı iyi bir çözüm olabilir:

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

Çıktı:

a b c 

6

std::copyEk iz süren ayırıcı kullanarak ama olmadan

std::copy(İlk olarak @JoshuaKravtiz yanıtında kullanıldığı gibi) kullanan ancak son öğeden sonra ek bir ayırıcı ayırıcı kullanmadan alternatif / değiştirilmiş bir yaklaşım :

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

Özel bir POD türündeki kaba uygulanan örnek kullanım:

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

aşırı yük operatörü <<:

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

Kullanımı:

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

3

İki sorun görüyorum. Belirtildiği gibi for (x = 17; isalpha(firstsquare); x++), sonsuz bir döngü vardır ya da hiç çalıştırılmaz ve ayrıca if (entrance == 'S')giriş karakteri 'S'den farklıysa, yol vektörüne itilen hiçbir şey boş olmaz ve ekranda hiçbir şey yazdırmaz. İkinci kontrolü path.empty()veya yazdırmayı test edebilirsiniz path.size().

Her iki durumda da, vektör yerine dize kullanmak daha iyi olmaz mıydı? Dizi gibi dizi içeriğine de erişebilir, karakterleri arayabilir, alt dizeleri çıkarabilir ve dizeyi kolayca (döngü olmadan) yazdırabilirsiniz.

Hepsini dizelerle yapmak, daha az kıvrımlı bir şekilde yazmanın ve sorunu daha kolay tespit etmenin yolu olabilir.


3

Bu cevap Zorawar'ın cevabına dayanıyor, ama orada bir yorum bırakamadım.

Cbegin ve cend komutlarını kullanarak auto (C ++ 11) / typedef sürüm sabitini yapabilirsiniz.

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

1

C ++ 11'de

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

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

Bu cevap, halihazırda var olan cevaplara kıyasla ek bilgi vermez.
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
Selam! Stackoverflow'a hoş geldiniz. Kod pasajına ne yaptığınıza ve soruyu nasıl cevapladığına dair bazı açıklamalar ekleyebilmeniz harika olurdu.
Slabgorb


0

Bu benim için çalıştı:

    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

İlgilenenler için: Her iki dünyanın en iyisini alan, her türlü aralığa daha genelleştirilmiş ve aritmetik olmayan türlerin (dize benzeri türler için istenen) tırnak içine alan genelleştirilmiş bir çözüm yazdım. Ayrıca, bu yaklaşımın herhangi bir ADL sorunu olmamalı ve aynı zamanda 'sürprizlerden' kaçınılmalıdır (çünkü duruma göre açıkça eklendiğinden):

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

Artık her aralıkta kullanımı oldukça kolay:

std::cout << range_out{ my_vector };

İp benzeri çek, iyileştirme için yer bırakır. Ayrıca static_assertönlemek için çözümümde kontrol var std::basic_string<>, ama basitlik için burada bıraktım.


-1

Kendi fonksiyonunuzu yazabilirsiniz:

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

-1

Döngüsüz tek gömlek isteyen insanlar için:

Hiç kimsenin buna sahip olmadığına inanamıyorum, ama belki de daha fazla C benzeri yaklaşım nedeniyle. Neyse, bir tek poşetine, bir döngü olmadan bunu tamamen güvenlidir görevini üstlenen bu std::vector<char>boşlukla sonlandırılmış:

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

Ama ostream@Zorawar'ın önerdiği gibi, sadece güvenli olmak için bunu operatöre sararım:

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

printfBunun yerine benzer davranışlar elde edebiliriz :

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

NOT:

Aşırı yüklenmiş ostreamoperatörün vektörü sabit olmayan olarak kabul etmesi gerekir. Bu, programı güvensiz hale getirebilir veya yanlış kullanılabilir kod getirebilir. Ayrıca, null karakter eklenmiş olduğundan, yeniden tahsis std::vectoredilebilir. Bu nedenle yineleyicilerle for-loop kullanmak daha hızlı olacaktır.


1
1. fprintf(stdout, "%s\n", &test[0]);farklı değildir std::cout << test.data(), her ikisi de bir boş sonlandırılmış vektör gerektirir. 2. Doğru operandı değiştiren " << Bunu ostream operatörüne sararım" operatörü çok kötü bir fikirdir.
HolyBlackCat

fprintf(stdout, "%s\n", &test[0]);Hiç bana herhangi bir sorun vermeden kod uzun süre kullandım . İlginç! Ve ben de vektör değiştirmek için o kadar güzel değil kabul ostreamoperatörü, ama ikisi de manuel döngü sevmediğim ve kullanan yineleyiciler. Her nasılsa std::vector<char>standart bir kütüphaneyi yazdırmak gibi basit işlemler için bunları saklamak istiyorum. Ancak C ++ sürekli gelişiyor, yakında gelebilir.
ぐ イ き ん ぐ
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.