如何打印出向量的内容?


281

我想在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;
    }
}

如何将矢量的内容打印到屏幕上?


1
为什么它“不起作用”?
默认值

Answers:


392

纯粹是为了回答您的问题,您可以使用迭代器:

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

如果要在for循环中修改向量的内容,请使用iterator而不是const_iterator

但是还有很多可以说的。如果您只想使用一个答案,可以在这里停止;否则,请继续阅读。

自动(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)。

在这种情况下,您也可以只使用a typedef(不限于C ++ 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会这样做,但要点是,该类是设计用于针对较复杂的对象执行此工作的对象,而该解决方案并不理想。

基于范围的for循环(C ++ 11)

请参阅Jefffrey的解决方案。在C ++ 11(及更高版本)中,您可以使用新的基于范围的for循环,如下所示:

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

由于path是项的向量(显式地std::vector<char>),所以对象i是向量的项的类型(即,显式地,它是类型char)。该对象i具有一个值,该值是该path对象中实际项目的副本。因此,i循环中的所有更改都不会保留下来path。此外,如果您想强制执行,你不希望能够改变的复制价值的事实,i在循环中,你可以强制的类型iconst 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 << ' ';

std :: copy

参见约书亚的答案。您可以使用STL算法std::copy将向量内容复制到输出流上。如果您愿意的话,这是一个很好的解决方案(此外,它非常有用,不仅在打印矢量内容的情况下)。

std :: for_each

请参阅Max的解决方案std::for_each在这种简单情况下,使用可能会显得过头,但是如果您想做的不仅仅是打印到屏幕上,这是一个非常有用的解决方案:使用std::for_each可以对向量内容执行任何(明智的)操作。

重载ostream :: operator <<

请参阅Chris的答案,它是对其他答案的补充,因为您仍然需要在重载中实现上述解决方案之一。在他的示例中,他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如果您尝试访问向量之外的元素,则不会抛出异常operator[]。我最初仅添加此评论,是为了提及一些可能有人会知道的有用信息。我现在没有区别。因此,本增编。


如果您是从循环0通过vector::size()与载体不是在循环中修改的,没有必要使用at(),并承担额外的边界检查开销。也就是说,我会按照您的建议使用迭代器。
Ed S.

1
@Ed:是的,at如果循环中没有任何东西可以修改向量,那么就没有意义了,但是我想我提到了它,以防万一向量在循环中修改了(不建议这样),因为它永远不会提及,至少了解它可能很有用。
Zorawar 2012年

可以将基于范围的for循环重写为使用引用,这对于大型子对象可能非常重要,如下所示:for (auto const &i: path) std::cout << i << ' ';
underscore_d

@underscore_d:谢谢。我已经清理了该部分,希望现在既完整又清晰一些。
Zorawar

“重载运算符<<”不是一个好的解决方案;至少一个重载运算符的操作数应该是您的程序定义的类,因为依赖于参数的查找
MM

218

一个简单的方法是使用标准复制算法

#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(又称控制台输出)是我们要写入的流,而空格字符(" ")是我们要在向量中存储的每个元素之间打印的内容。

此标准算法功能强大,其他许多算法也是如此。标准库为您提供的强大功能和灵活性就是让它如此强大的原因。试想:你可以打印一个矢量到控制台只有一个行代码。您不必使用分隔符来处理特殊情况。您无需担心for循环。标准库可为您完成所有工作。


3
如果我的向量是类型的怎么办vector<pair<int, struct node>>?如何使用以上方法打印此矢量?
mtk 2015年

6
分隔符字符串写每个元素之后,而不是每个元素之间,即也写在最后一个元素之后。如果只希望在特殊情况之间进行分隔,即可能需要处理特殊情况。
Quigi 2015年

2
@mtk可以operator<<为您的特定对<> 声明一个函数。
ShoeLace

添加了一个答案,该答案显示了类似的方法,但考虑到以上关于额外尾随分隔符的@Quigi:s注释。
dfri

@ShoeLace还有别的办法吗?
thegreatcoder

69

在C ++ 11中,您现在可以使用基于范围的for循环

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

仅当向量的大小在循环范围的主体中未更改时,此方法才有效。
布莱恩(Brian)

8
@BrianP。对。打印容器的元素不会更改容器的范围。
鞋2014年

在这里最好使用c作为值副本或const引用,以避免复制元素?
kleinfreund

@kleinfreund取决于向量的内容。例如,对于chars 的向量,通过常量引用传递实际上比按值传递代价更高。但是这里我们谈论的是超微优化。
鞋2015年

43

我认为做到这一点的最好方法是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}

3
将v.size()-1存储为int可能会降低精度。我通过接受的同行评审编辑(stackoverflow.com/revisions/23397700/5)修复了此问题,但此后再次对其进行了编辑,以恢复可能的精度损失。我猜在实践中并没有太大关系,因为矢量通常不会那么大。
JDiMatteo 2014年

不将其存储为变量会降低代码的可读性,这是我不同意的一部分。我已将的类型更改lastsize_t
克里斯·雷德福德

size_t last = v.size() - 1;看起来很多余,您可以if (i) out << ", ";out << v[i]; 链接
弗拉基米尔·加马利扬(Fladimir Gamalyan)2016年

3
ADL找不到此运算符,因为它不在其任何参数的命名空间中。因此它将被任何其他名称空间的隐藏operator<<示例
MM

如果要执行此操作,为什么要循环中if (i != last) 每次都进行测试?相反,如果容器不为空,则(a)发送第一个元素,然后(b)循环发送 其余元素,首先打印分隔符(作为前缀)。不需要内部循环测试(除了循环条件本身)。只需要进行一次环外测试。
WhozCraig

22

for_each+ lambda表达式如何:

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

当然,基于范围的for是解决此具体任务的最优雅的解决方案,但此解决方案还提供了许多其他可能性。

说明

for_each算法接受一个输入范围和一个可调用对象,在范围的每个元素上调用此对象。的输入范围是由两个限定的迭代器。阿可调用对象可以是一个函数,函数指针,一类过载的目的() operator或作为在这种情况下,一个拉姆达表达。此表达式的参数与vector中元素的类型匹配。

此实现的优点在于,您可以从lambda表达式中获得强大的功能-您可以将这种方法用于不仅仅是打印矢量的更多事情。


11

只需将容器复制到控制台即可。

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

应该输出:

1 2 3 4

8

问题可能出在前一个循环中:(x = 17; isalpha(firstsquare); x++)。此循环根本不会运行(如果firstsquare是非Alpha),或者将永远运行(如果它是Alpha)。原因是firstsquare不会随着x增加而改变。


7

在C ++ 11中,基于范围的for循环可能是一个很好的解决方案:

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

输出:

a b c 

6

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

5

重载运算符<<:

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

3

我看到两个问题。正如所指出的那样,for (x = 17; isalpha(firstsquare); x++)要么存在无限循环,要么根本不执行,而且if (entrance == 'S')如果输入字符与“ S”不同,则不会将任何内容压入路径矢量,从而使其为空,从而在屏幕上不打印任何内容。您可以测试后者检查path.empty()或打印path.size()

无论哪种方式,使用字符串而不是向量都更好吗?您也可以像访问数组一样访问字符串内容,查找字符,提取子字符串并轻松打印字符串(无循环)。

用字符串完成所有操作可能是使它以较少混淆的方式编写并更容易发现问题的方式。


3

该答案基于Zorawar的答案,但我不能在此处发表评论。

您可以使用cbegin和cend来使auto(C ++ 11)/ typedef版本为const

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

1

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

与已经存在的答案相比,该答案没有提供任何其他信息。
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
嗨!欢迎来到stackoverflow。如果您可以在代码片段中包含一些有关您正在做什么以及它如何回答问题的解释,那将是很好的。
Slabgorb


0

这为我工作:

    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

对于那些感兴趣的人:我写了一个通用的解决方案,它兼顾了两个方面的优点,更广泛地适用于任何类型的范围,并在非算术类型(如字符串型类型)周围加引号。此外,此方法不应有任何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<>,但为简单起见,在此将其省略。


-1

您可以编写自己的函数:

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

-1

对于想要单线无循环的人:

我不敢相信没有人会这样做,但这也许是因为它更像C。不管怎么说,这是完全安全的这样做没有一个循环,在一个班轮,基于假设std::vector<char>是空值终止:

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

但是ostream为了安全起见,我会像@Zorawar建议的那样将其包装在运算符中:

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

我们可以通过printf改为使用类似的行为:

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

注意:

重载的ostream运算符需要将向量接受为非常量。这可能会使程序不安全或引入错误的代码。另外,由于附加了空字符,std::vector可能会发生重新分配。因此,使用带有迭代器的for循环可能会更快。


1
1. fprintf(stdout, "%s\n", &test[0]);与没什么不同std::cout << test.data(),两者都需要以null终止的向量。2. “但是我将其包装在ostream运算符中” <<运算符,修改正确的操作数是一个非常糟糕的主意。
HolyBlackCat

我已经fprintf(stdout, "%s\n", &test[0]);在代码中使用了很长一段时间,而这却没有给我带来任何麻烦。有趣!我同意在ostream操作符中修改向量不是很好,但是我不喜欢手动循环使用迭代器。对于某种简单的操作,例如以某种方式打印std::vector<char>标准库,我感觉应该将这些东西隐藏起来。但是C ++不断发展,可能很快就会到来。
ワイきんぐ
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.