Как распечатать содержимое вектора?


Я хочу, чтобы распечатать содержимое вектора в C++, вот что у меня:

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

Как вывести содержимое вектора на экран?

13 205

13 ответов:

чисто, чтобы ответить на ваш вопрос, вы можете использовать итератор:

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

если вы хотите изменить содержимое вектора в цикле for, то используйте iterator, а не const_iterator.

но есть еще много чего, что можно сказать об этом. Если вам просто нужен ответ, который вы можете использовать, тогда вы можете остановиться здесь; в противном случае читайте дальше.

auto (C++11)/typedef

это не другое решение, а дополнение к вышесказанному iterator решение. Если вы используете стандарт C++11 (или позже), то вы можете использовать auto ключевое слово, чтобы помочь удобочитаемости:

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

но типа i будет не-const (т. е., компилятор будет использовать std::vector<char>::iterator тип i).

в этом случае, вы могли бы также просто использовать typedef (не ограничивается C++11, и очень полезно использовать в любом случае):

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

счетчик

вы можете, конечно, использовать тип integer в запишите свою позицию в for петли:

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

если вы собираетесь сделать это, лучше использовать типы элементов контейнера, если они доступны и подходят. std::vector имеет тип члена под названием size_type для этого задания: это тип, возвращаемый size метод.

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

почему бы просто не использовать тег iterator решение? Для простых случаев вы могли бы также, но дело в том, что iterator класс-это объект, предназначенный для выполнения этой работы более сложные объекты, где это решение не будет идеальным.

диапазон на основе цикла (C++11)

посмотреть Jefffrey это. В C++11 (и более поздних версиях) вы можете использовать новый диапазон на основе for петля, которая выглядит так:

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

С path - это вектор элементов (явно std::vector<char>), объект i имеет тип элемента вектора (т. е. явно имеет тип char). Объект i имеет значение это копия фактического элемента в , вы можете использовать ссылку:

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

и даже если вы не хотите изменить path, если копирование объектов дорого, вы должны использовать ссылку const вместо копирования по значению:

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

std:: copy

посмотреть Джошуа. Вы можете использовать алгоритм STL std::copy для копирования векторного содержимого в выходной поток. Это элегантное решение, если вам удобно с ним (и кроме того, это очень полезно, а не только в этом случае печати содержимого вектор.)

std:: for_each

посмотреть Макс. Используя std::for_each является излишним для этого простого сценария, но это очень полезное решение, если вы хотите сделать больше, чем просто печать на экран: с помощью std::for_each позволяет сделать любой (разумная) операция над векторным содержимым.

перегрузка ostream:: operator

посмотреть ответ Криса, это скорее дополнение к другим ответы так как вам все равно нужно будет реализовать одно из решений выше в перегрузке. В своем примере он использовал счетчик в for петли. Например, вот как можно быстро использовать Джошуа:

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

использование любого из других решений должно быть простым.

вывод

любым из представленных здесь решений будет работать. Это зависит от вас и кода, на котором один из них является "лучшим". Что-нибудь более подробное, чем это, вероятно, лучше оставить для другого вопроса, где плюсы/минусы могут быть правильно оценены; но, как всегда, предпочтение пользователя всегда будет играть определенную роль: ни одно из представленных решений не является неправильным, но некоторые из них будут выглядеть лучше для каждого отдельного кодера.

дополнительное соглашение

это расширенное решение более раннего я опубликовал. Поскольку этот пост продолжал привлекать внимание, я решил расширить его и обратиться к другим отличным решениям, которые были размещены здесь. Мой оригинальный пост было замечание, в котором говорилось, что если вы были намереваясь изменить свой вектор внутри for цикл тогда есть два метода, предоставляемые std::vector для доступа к элементам:std::vector::operator[], который не делает проверку границ, и std::vector::at который выполняет проверку границ. Другими словами,at бросит, если вы попытаетесь получить доступ к элементу вне вектора и

гораздо проще сделать это со стандартным алгоритм копирования:

#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-это то, что называется итератор адаптер. Он шаблонизирован по типу для вывода в поток (в этом случае char). cout (aka console output) - это поток, в который мы хотим записать, и символ пробела (" ") - это то, что мы хотим напечатать между каждым элементом, хранящимся в векторе.

этот стандартный алгоритм могущественны и многие другие. Сила и гибкость стандартная библиотека дает вам то, что делает это так здорово. Только представьте: вы можете распечатать вектор на консоль с помощью только один строка кода. Вам не нужно иметь дело с особыми случаями с символом разделителя. Вам не нужно беспокоиться о for-loops. Стандартная библиотека делает все это за вас.

в C++11 теперь можно использовать диапазон на основе цикла:

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

Я думаю, что лучший способ сделать это-просто перегрузка operator<< добавив эту функцию в свою программу:

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

затем вы можете использовать << оператор на любом возможном векторе, предполагая, что его элементы также имеют ostream& operator<< определил:

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;

выходы:

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

как о for_each + лямбда-выражение:

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

конечно, a принимает входной диапазон и callable объект, вызывая этот объект на каждом элементе диапазона. Ан входной диапазон определяется на два итераторы. А callable объект может быть функция, указатель на функцию, объект класса, который перегружает () operator или как в данном случае, лямбда-выражение. Параметр для этого выражения соответствует типу элементов из вектора.

красота этой реализации-это сила, которую вы получаете от лямбда - выражений-вы можете использовать этот подход для гораздо большего, чем просто печать вектора.

проблема, вероятно, в предыдущем цикле: (x = 17; isalpha(firstsquare); x++). Этот цикл будет работать не на всех (если firstsquare не-альфа) или будет работать вечно (если это альфа). Причина в том, что firstsquare не меняется как x увеличивается.

просто скопируйте контейнер на консоль.

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

следует вывод :

1 2 3 4

в C++11 цикл for на основе диапазона может быть хорошим решением:

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

выход:

a b c 

Я вижу две проблемы. Как указано в for (x = 17; isalpha(firstsquare); x++) тут либо бесконечный цикл, либо не выполняется вообще, а также в if (entrance == 'S') если входной символ отличается от "S", то ничего не нажимается на вектор пути, делая его пустым и, таким образом, ничего не печатая на экране. Вы можете проверить последнюю проверку на path.empty() или печати path.size().

в любом случае, не лучше ли использовать строку вместо вектора? Вы можете получить доступ к содержимому строки, как массив, а также, искать символы, извлечь подстроки и распечатать строку легко (без цикла).

выполнение всего этого со строками может быть способом, чтобы он был написан менее запутанным способом и легче определить проблему.

перегрузка оператора

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

использование:

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

этот ответ базируется на ответ от Зоравар, но я не могу оставить там комментарий.

вы можете сделать auto (C++11)/typedef версия const с помощью cbegin и cend вместо

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

используя std::copy но без лишних конечный разделитель

альтернативный / модифицированный подход с использованием std::copy (как первоначально использовались в @JoshuaKravtiz ответ) но без включения дополнительного трейлинг-разделителя после последнего элемента:

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

пример использования применяется к контейнеру пользовательского типа 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;
}

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