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

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

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

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

Ответ 1

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

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 (С++ 11)/typedef

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

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

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

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

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

Счетчик

Вы можете, конечно, использовать целочисленный тип для записи вашей позиции в цикле 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 - это объект, предназначенный для выполнения этой задачи для более сложных объектов, где это решение не будет идеальным.

диапазон для цикла (С++ 11)

Смотрите решение Джеффри. В С++ 11 (и позже) вы можете использовать новый цикл for на основе диапазона, который выглядит следующим образом:

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

Так как path является вектором элементов (явно std::vector<char>), объект i имеет тип элемента вектора (т.е. явно, имеет тип char). Объект i имеет значение, которое является копией фактического элемента в объекте path. Таким образом, все изменения в i в цикле не сохраняются в path. Кроме того, если вы хотите усилить тот факт, что вы не хотите изменять скопированное значение i в цикле, вы можете заставить тип i быть const char следующим образом:

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

Если вы хотите изменить элементы в path, вы можете использовать ссылку:

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

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

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

станд:: копия

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

станд:: for_each

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

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

Смотрите ответ Криса, это больше дополняет другие ответы, так как вам все равно придется реализовать одно из вышеперечисленных решений при перегрузке. В своем примере он использовал счетчик в цикле for. Например, вы можете быстро использовать решение 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;
}

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

Заключение

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

добавление

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

Ответ 2

Более простой способ сделать это - стандартный алгоритм копирования:

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

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

Ответ 4

Я думаю, что лучший способ сделать это - просто перегрузить 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}

Ответ 5

Как насчет выражения for_each + лямбда:

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

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

Объяснение

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

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

Ответ 6

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

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

Должен вывести:

1 2 3 4

Ответ 7

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

Ответ 8

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

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

Выход:

a b c 

Ответ 9

Использование 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;
}

Ответ 10

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

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

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

Ответ 11

оператор перегрузки <:

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

Ответ 12

Этот ответ основан на ответе от Zorawar, но я не мог оставить комментарий там.

Вы можете сделать автоматическую (С++ 11)/typedef версию const с помощью cbegin и cend вместо

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

Ответ 13

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

Ответ 14

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

Ответ 15

Это сработало для меня:

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

Ответ 16

Для тех, кто интересуется: я написал обобщенное решение, которое использует лучшее из обоих миров, более обобщенно для любого типа диапазона и помещает кавычки вокруг неарифметических типов (желательно для строковых типов). Кроме того, этот подход не должен иметь проблем с ADL, а также избегать "сюрпризов" (поскольку он добавляется явно в каждом конкретном случае):

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

Теперь его довольно легко использовать в любом диапазоне:

std::cout << range_out{ my_vector };

Струнный чек оставляет место для улучшения. У меня также есть проверка static_assert в моем решении, чтобы избежать std::basic_string<>, но я упустил это здесь для простоты.

Ответ 17

Вы можете написать свою собственную функцию:

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