Как распечатать содержимое вектора?
Я хочу распечатать содержимое вектора в С++, вот что у меня есть:
#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
(или консольный вывод) - это поток, который мы хотим записать, и символ пробела (" "
) - это то, что мы хотим напечатать между каждым элементом, хранящимся в векторе.
Этот стандартный алгоритм является мощным, и многие другие. Сила и гибкость, предоставляемые стандартной библиотекой, - вот что делает ее такой замечательной. Представьте себе: вы можете напечатать вектор на консоли только с помощью одной строки кода. Вам не нужно иметь дело со специальными случаями с символом разделителя. Вам не нужно беспокоиться о циклах. Стандартная библиотека делает все для вас.
Ответ 3
В С++ 11 теперь вы можете использовать диапазон для цикла:
for (auto const& c : path)
std::cout << c << ' ';
Ответ 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;
}