Como imprimir o conteúdo de um vetor?

281

Eu quero imprimir o conteúdo de um vetor em C ++, aqui está o que eu tenho:

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

Como imprimo o conteúdo do vetor na tela?

forthewinwin
fonte
1
Por que não está funcionando"?
Padrão

Respostas:

392

Para responder sua pergunta, você pode usar um iterador:

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

Se você deseja modificar o conteúdo do vetor no loop for, use em iteratorvez deconst_iterator .

Mas há muito mais a ser dito sobre isso. Se você quer apenas uma resposta que possa usar, pode parar por aqui; caso contrário, continue a ler.

auto (C ++ 11) / typedef

Esta não é outra solução, mas um complemento para a iteratorsolução acima . Se você estiver usando o padrão C ++ 11 (ou posterior), poderá usar a autopalavra-chave para ajudar na legibilidade:

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

Mas o tipo de iserá não-const (ou seja, o compilador usará std::vector<char>::iteratorcomo o tipo de i).

Nesse caso, você também pode usar um typedef(não restrito ao C ++ 11 e muito útil para usar):

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

contador

Obviamente, você pode usar um tipo inteiro para registrar sua posição no forloop:

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

Se você fizer isso, é melhor usar os tipos de membros do contêiner, se estiverem disponíveis e adequados. std::vectorpossui um tipo de membro chamado size_typepara este trabalho: é o tipo retornado pelo sizemétodo

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

Por que não usar isso sobre a iteratorsolução? Em casos simples, você pode também, mas o ponto é que a iteratorclasse é um objeto projetado para fazer esse trabalho em objetos mais complicados, onde essa solução não será ideal.

loop for baseado em intervalo (C ++ 11)

Veja a solução de Jefffrey . No C ++ 11 (e posterior), você pode usar o novo forloop baseado em intervalo , que se parece com isso:

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

Como pathé um vetor de itens (explicitamente std::vector<char>), o objeto ié do tipo do item do vetor (ou seja, explicitamente, é do tipo char). O objeto item um valor que é uma cópia do item real no pathobjeto. Portanto, todas as alterações ino loop não são preservadas por pathsi só. Além disso, se você deseja reforçar o fato de que não deseja alterar o valor copiado ino loop, pode forçar o tipo ia ser const charassim:

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

Se você deseja modificar os itens path, pode usar uma referência:

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

e mesmo se você não quiser modificar path, se a cópia de objetos for cara, use uma referência const em vez de copiar por valor:

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

std :: copy

Veja a resposta de Josué . Você pode usar o algoritmo STL std::copypara copiar o conteúdo do vetor no fluxo de saída. Esta é uma solução elegante se você se sentir confortável com ela (e, além disso, é muito útil, não apenas no caso de imprimir o conteúdo de um vetor).

std :: for_each

Veja a solução de Max . O uso std::for_eaché um exagero neste cenário simples, mas é uma solução muito útil se você deseja fazer mais do que apenas imprimir na tela: o uso std::for_eachpermite fazer qualquer operação (sensível) no conteúdo do vetor.

sobrecarga ostream :: operator <<

Veja a resposta de Chris , isso é mais um complemento para as outras respostas, pois você ainda precisará implementar uma das soluções acima na sobrecarga. Em seu exemplo, ele usou um contador em um forloop. Por exemplo, é assim que você pode usar rapidamente a solução de 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;
}

O uso de qualquer uma das outras soluções deve ser direto.

conclusão

Qualquer uma das soluções apresentadas aqui funcionará. Depende de você e do código em qual é o "melhor". Qualquer coisa mais detalhada do que isso é provavelmente deixada para outra pergunta em que os prós / contras possam ser avaliados adequadamente; mas, como sempre, a preferência do usuário sempre desempenhará um papel: nenhuma das soluções apresentadas está errada, mas algumas parecerão mais agradáveis ​​para cada codificador individual.

termo aditivo

Esta é uma solução expandida de uma anterior que eu publiquei. Como esse post continuava recebendo atenção, decidi expandi-lo e me referir às outras excelentes soluções que foram publicadas aqui. Meu post original teve uma observação que mencionou que se você foram a intenção de modificar o seu vector dentro de um forcircuito, em seguida, existem dois métodos fornecidos pela std::vectora elementos de acesso: std::vector::operator[]o que não fazer verificação de limites, e std::vector::atque faz executar verificação de limites. Em outras palavras, atserá lançado se você tentar acessar um elemento fora do vetor e operator[]não o faria. Apenas adicionei esse comentário, originalmente, para mencionar algo que talvez seja útil saber se alguém já não o fez. E não vejo diferença agora. Daí este adendo.

Zorawar
fonte
Se você estiver fazendo um loop do 0meio vector::size()e o vetor não for modificado dentro do loop, não há necessidade de usar at()e incorrer nos limites adicionais de verificação de sobrecarga. Dito isto, eu iria com um iterador, como você sugere.
Ed S.
1
@ Ed: sim, não adianta usar atse nada no loop modifica o vetor, mas pensei em mencioná-lo apenas no caso de o vetor ser modificado no loop (não recomendado como isso pode ser) e porque nunca recebe um mencionar e pode ser útil, pelo menos, conhecê-lo.
Zorawar
O loop for baseado em intervalo pode ser reescrito para usar referências, o que pode ser importante no caso de grandes subobjetos, como a seguir:for (auto const &i: path) std::cout << i << ' ';
underscore_d
@underscore_d: obrigado. Limpei essa seção e espero que seja mais completa e um pouco mais clara agora.
Zorawar
"operador de sobrecarga <<" não é uma boa solução; pelo menos um operando do operador sobrecarregado deve ser uma classe definida pelo seu programa, por causa da pesquisa dependentes do argumento
MM
218

Uma maneira muito mais fácil de fazer isso é com o algoritmo de cópia padrão :

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

O ostream_iterator é o que chamamos de adaptador iterador . Ele é modelado sobre o tipo para imprimir no fluxo (neste caso, char). cout(também conhecido como saída do console) é o fluxo no qual queremos gravar e o caractere de espaço ( " ") é o que queremos imprimir entre cada elemento armazenado no vetor.

Esse algoritmo padrão é poderoso e muitos outros. O poder e a flexibilidade que a biblioteca padrão oferece são o que a torna tão excelente. Imagine: você pode imprimir um vetor no console com apenas uma linha de código. Você não precisa lidar com casos especiais com o caractere separador. Você não precisa se preocupar com loops. A biblioteca padrão faz tudo por você.

Joshua Kravitz
fonte
3
e se meu vetor fosse do tipo vector<pair<int, struct node>>. Como uso o método acima para imprimir esse vetor?
mtk
6
A string delimitadora é escrita após cada elemento, não entre, ou seja, também após o último. Isso pode exigir lidar com casos especiais, se você quiser apenas entre, como um separador.
Quigi 28/12/2015
2
@mtk, você pode declarar uma operator<<função para o seu par específico <>.
30817
Foi adicionada uma resposta mostrando uma abordagem semelhante, mas levando em consideração o comentário do @Quigi: s acima, sobre o separador de rastreamento extra.
DFRI
@ShoeLace Não existe outra maneira?
thegreatcoder
69

No C ++ 11, agora você pode usar um loop for baseado em intervalo :

for (auto const& c : path)
    std::cout << c << ' ';
Sapato
fonte
Isso funciona muito bem apenas se o tamanho do vetor não for alterado no corpo do intervalo de loop.
Brian
8
@BrianP. Sim. A impressão dos elementos de um contêiner não modifica o intervalo do contêiner.
Shoe
O que é preferível aqui - c como uma cópia de valor ou como uma referência const para evitar a cópia do elemento?
Kleinfreund
@kleinfreund Depende do conteúdo do vetor. Por exemplo, para um vetor de chars, é provável que a passagem por referência constante seja realmente mais cara do que por valor. Mas aqui estamos falando de super otimizações micro.
Shoe
43

Eu acho que a melhor maneira de fazer isso é sobrecarregar operator<<adicionando esta função ao seu programa:

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

Então você pode usar o <<operador em qualquer vetor possível, assumindo que seus elementos também tenham ostream& operator<<definido:

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;

Saídas:

{first, second, third}
{1, 0, 1, 0, 0}
{1, 2, 3, 4}
Chris Redford
fonte
3
Armazenar v.size () - 1 como int é uma possível perda de precisão. Corrigi isso em uma edição aceita revisada por pares ( stackoverflow.com/revisions/23397700/5 ), mas depois foi editada novamente restaurando a possível perda de precisão. Eu acho que isso não importa muito na prática, pois os vetores geralmente não são tão grandes.
JDiMatteo 9/10
Não armazená-lo como uma variável diminui a legibilidade do código, que faz parte da sua edição com a qual discordei. Eu mudei o tipo de lastpara size_t.
Chris Redford
size_t last = v.size() - 1;parece redundante, você pode usar a if (i) out << ", ";condição antes do out << v[i]; link
Vladimir Gamalyan
3
Esse operador não foi encontrado pelo ADL, pois não está no espaço para nome de nenhum de seus argumentos. Portanto, ele ficará oculto por qualquer outro espaço de nome operator<<. Exemplo
MM
Se você vai fazer isso, por que testar if (i != last) cada vez no loop? Em vez disso, se o contêiner não estiver vazio, (a) envie o primeiro elemento e, em seguida, (b) envie os elementos restantes em loop , imprimindo primeiro o separador (como prefixo). Nenhum teste de loop interno (além da própria condição do loop) é necessário. Apenas um teste fora de loop é necessário.
precisa saber é o seguinte
22

Que tal for_eachexpressão + lambda :

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

Obviamente, uma solução baseada em faixa é a solução mais elegante para essa tarefa concreta, mas essa também oferece muitas outras possibilidades.

Explicação

O for_eachalgoritmo pega um intervalo de entrada e um objeto que pode ser chamado, chamando esse objeto em todos os elementos do intervalo. Um intervalo de entrada é definido por dois iteradores . Um objeto que pode ser chamado pode ser uma função, um ponteiro para funcionar, um objeto de uma classe que sobrecarrega () operatorou, nesse caso, uma expressão lambda . O parâmetro para esta expressão corresponde ao tipo dos elementos do vetor.

A beleza dessa implementação é o poder que você obtém das expressões lambda - você pode usar essa abordagem para muito mais coisas do que apenas imprimir o vetor.

Maxim Chetrusca
fonte
10

Basta copiar o contêiner para o console.

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

Saída deve:

1 2 3 4
g24l
fonte
8

O problema é provavelmente no circuito anterior: (x = 17; isalpha(firstsquare); x++). Esse loop não será executado (se firstsquarenão for alfa) ou será executado para sempre (se for alfa). O motivo é que firstsquarenão muda conforme xé incrementado.

MSalters
fonte
7

No C ++ 11, um loop for baseado em intervalo pode ser uma boa solução:

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

Resultado:

a b c 
stupidgoddamnjerk
fonte
6

Usando std::copy mas sem separador de arrasto extra

Uma abordagem alternativa / modificada usando std::copy(como originalmente usado na resposta @JoshuaKravtiz ), mas sem incluir um separador de arrasto adicional após o último 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;
}

Exemplo de uso aplicado ao contêiner de um tipo POD personalizado:

// 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;
}
dfri
fonte
3

Eu vejo dois problemas. Como apontado em, for (x = 17; isalpha(firstsquare); x++)existe um loop infinito ou nunca é executado, e também if (entrance == 'S')se o caractere de entrada for diferente de 'S', nada será empurrado para o vetor de caminho, deixando-o vazio e imprimindo nada na tela. Você pode testar a última verificação path.empty()ou impressãopath.size() .

De qualquer maneira, não seria melhor usar uma string em vez de um vetor? Você também pode acessar o conteúdo da string como uma matriz, buscar caracteres, extrair substrings e imprimir a string facilmente (sem um loop).

Fazer tudo com strings pode ser a maneira de escrever de uma maneira menos complicada e mais fácil de identificar o problema.

miguelbernadi
fonte
3

operador de sobrecarga <<:

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
ivanmara
fonte
3

Esta resposta é baseada na resposta de Zorawar, mas não pude deixar um comentário lá.

Você pode criar a versão auto (C ++ 11) / typedef const usando cbegin e cend

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

Em 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] << ' ';
Akash Kandpal
fonte
Esta resposta não fornece nenhuma informação adicional em comparação com as respostas já existentes.
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;
}``
}
Bishal B Sonar
fonte
2
Oi! Bem-vindo ao stackoverflow. Seria ótimo se você pudesse incluir no trecho de código algumas explicações sobre o que está fazendo e como ele responde à pergunta.
Slabgorb
2
Você pode ler: Por que não devo # incluir <bits / stdc ++. H>?
Blastfurnace 27/06/19
0

Isso funcionou para mim:

    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;
    }
Tempi Skat
fonte
0

Para aqueles que estão interessados: eu escrevi uma solução generalizada que tira o melhor dos dois mundos, é mais generalizada para qualquer tipo de intervalo e coloca aspas em torno de tipos não aritméticos (desejados para tipos semelhantes a cordas). Além disso, essa abordagem não deve ter problemas de ADL e também evitar 'surpresas' (uma vez que é adicionada explicitamente caso a 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;
}

Agora é bastante fácil de usar em qualquer faixa:

std::cout << range_out{ my_vector };

A verificação semelhante a uma corda deixa espaço para melhorias. Também tenho static_assertcheck-in na minha solução para evitar std::basic_string<>, mas deixei-o aqui por simplicidade.

darune
fonte
-1

Você pode escrever sua própria função:

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

Para pessoas que desejam one-liners sem loops:

Não acredito que ninguém tenha pensado nisso, mas talvez seja por causa da abordagem mais parecida com o C. De qualquer forma, é perfeitamente seguro fazer isso sem um loop, em uma linha, ASSUMINDO que o terminalstd::vector<char> é nulo:

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

Mas eu colocaria isso no ostreamoperador, como sugeriu @Zorawar, apenas para garantir:

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

Podemos obter um comportamento semelhante usando printf:

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

NOTA:

O ostreamoperador sobrecarregado precisa aceitar o vetor como não const. Isso pode tornar o programa inseguro ou introduzir código incorreto. Além disso, como o caractere nulo é anexado, uma realocação do std::vectorpode ocorrer. Portanto, o uso de loops for iteradores provavelmente será mais rápido.

ワ イ き ん ぐ
fonte
1
1. fprintf(stdout, "%s\n", &test[0]);não é diferente de std::cout << test.data(), ambos requerem um vetor terminado em nulo. 2. "Mas eu colocaria isso no operador ostream" << operador que modifica o operando certo é uma péssima idéia.
21819 HolyBlackCat
Eu uso fprintf(stdout, "%s\n", &test[0]);há muito tempo no código sem que isso me cause problemas. Interessante! E eu concordo que não é tão bom modificar o vetor no ostreamoperador, mas não gosto de fazer loop manualmente e usar iteradores. De alguma forma, acho que para operações simples, como imprimir uma std::vector<char>biblioteca padrão, deve esconder essas coisas. Mas o C ++ está se desenvolvendo constantemente, pode vir em breve.
precisa