如何打印出矢量的内容?

时间:2012-05-25 07:13:53

标签: c++ vector output stdvector cout

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

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

21 个答案:

答案 0 :(得分:323)

纯粹回答你的问题,你可以使用迭代器:

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

计数器

当然,您可以使用整数类型在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's solution。在C ++ 11(及更高版本)中,您可以使用新的基于范围的for循环,如下所示:

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

由于path是项目的向量(显式为std::vector<char>),因此对象i是向量项的类型(即,显式地,它的类型为{{ 1}})。对象char的值为i对象中实际项的副本。因此,循环中path的所有更改都不会保留在i本身中。此外,如果您希望强制执行以下事实:您不希望在循环中更改path的复制值,则可以强制i的类型为{{1像这样:

i

如果您想修改const char中的项目,可以使用参考:

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

即使你不想修改path,如果复制对象很昂贵,你应该使用const引用而不是按值复制:

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

的std ::复制

Joshua's answer。您可以使用STL算法path将矢量内容复制到输出流。如果您对它感到满意,这是一个优雅的解决方案(此外,非常有用,不仅仅是在打印矢量内容的情况下)。

的std ::的for_each

Max's solution。使用for (const auto& i: path) std::cout << i << ' '; 对于这个简单的场景来说太过分了,但如果你想做的不仅仅是打印到屏幕上,它是一个非常有用的解决方案:使用std::copy可以让你做任何 (明智的)对矢量内容的操作。

overload ostream :: operator&lt;&lt;

请参阅Chris's answer,这是对其他答案的补充,因为您仍需要在重载中实现上述解决方案之一。在他的例子中,他在std::for_each循环中使用了一个计数器。例如,您可以快速使用Joshua's solution

std::for_each

任何其他解决方案的使用应该是直截了当的。

结论

此处介绍的任何解决方案均可使用。这取决于你和一个“最好”的代码。任何比这更详细的东西最好留给另一个可以正确评估利弊的问题;但是一如既往,用户偏好总会发挥作用:所提出的解决方案都没有错,但有些人会对每个编码器看起来更好。

附录

这是我发布的早期版本的扩展解决方案。由于该帖子一直受到关注,我决定对其进行扩展,并参考此处发布的其他优秀解决方案。我的原帖有一句话提到,如果 打算在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; } 提供两种方法来访问元素:{{ 1}}不执行边界检查,for执行边界检查。换句话说,如果您尝试访问向量之外的元素而std::vector不会,则std::vector::operator[]将抛出。我最初只是添加了这条评论,为了提及一些可能有用的东西,知道是否有人已经没有。我现在看不出任何区别。因此这个附录。

答案 1 :(得分:199)

更简单的方法是使用标准copy algorithm

#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循环。标准库为您完成所有工作。

答案 2 :(得分:64)

在C ++ 11中,您现在可以使用range-based for loop

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

答案 3 :(得分:37)

我认为最好的方法是通过在程序中添加此函数来重载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}

答案 4 :(得分:18)

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_each算法采用输入范围可调用对象,在范围的每个元素上调用此对象。 输入范围由两个迭代器定义。 可调用对象可以是一个函数,一个指向函数的指针,一个重载() operator的类的对象,或者在这种情况下,一个lambda表达式。此表达式的参数与vector中的元素类型相匹配。

这种实现的优点在于你从lambda表达式中获得的力量 - 除了打印向量之外,你可以使用这种方法来做更多的事情。

答案 5 :(得分:8)

问题可能出在前一个循环中:(x = 17; isalpha(firstsquare); x++)。这个循环根本不会运行(如果firstsquare是非alpha)或将永远运行(如果它是alpha)。原因是firstsquarex递增时不会改变。

答案 6 :(得分:8)

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

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 :(得分:6)

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

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

输出:

a b c 

答案 8 :(得分:4)

使用child-2但没有额外的尾随分隔符

使用std::copy的替代/修改方法(最初在@JoshuaKravtiz answer中使用)但在最后一个元素之后不包括额外的尾随分隔符:

parent

应用于自定义POD类型容器的示例用法:

std::copy

答案 9 :(得分:3)

我看到两个问题。正如在for (x = 17; isalpha(firstsquare); x++)中指出的那样,无论是无限循环还是根本不执行,如果入口角色不同于if (entrance == 'S'),则path.empty()也是如此。然后没有任何东西被推到路径向量,使其为空,因此在屏幕上不打印任何内容。您可以测试后者检查path.size()或打印{{1}}。

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

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

答案 10 :(得分:3)

这个答案是基于Zorawar的答案,但我无法在那里发表评论。

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

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

答案 11 :(得分:3)

重载运算符&lt;&lt;:

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 :(得分: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] << ' ';

答案 13 :(得分:0)

您可以编写自己的函数:

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

答案 14 :(得分: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;
}``
}

答案 15 :(得分: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;
    }

答案 16 :(得分: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<>,但是为了简单起见,在此省略了它。

答案 17 :(得分:0)

模板集合:

应用std::cout <<std::to_string

std::vectorstd::arraystd::tuple

由于在cpp中打印矢量结果出乎意料的繁重工作(至少与该任务的基本操作相比),并且在与其他容器一起工作时,又一次跨过同一问题,这是一个更通用的解决方案。

模板集合内容

此模板集合处理3种容器类型: std::vectorstd::arraystd::tuple。 它为这些定义了std::to_string(),并可以通过std::cout << container;直接将它们打印出来。

此外,它为std::string << container定义了<<操作符。 这样,就可以以紧凑的方式构造包含这些容器类型的字符串。

来自

std::string s1 = "s1: " + std::to_string(arr) + "; " + std::to_string(vec) + "; " + std::to_string(tup);

我们去

std::string s2 = STR() << "s2: " << arr << "; " << vec << "; " << tup;

代码

您可以以交互方式测试此代码:here

#include <iostream>
#include <string>
#include <tuple>
#include <vector>
#include <array>

namespace std
{   
    // declations: needed for std::to_string(std::vector<std::tuple<int, float>>)
    std::string to_string(std::string str);
    std::string to_string(const char *str);
    template<typename T, size_t N>
    std::string to_string(std::array<T, N> const& arr);
    template<typename T>
    std::string to_string(std::vector<T> const& vec);
    template<typename... Args>
    std::string to_string(const std::tuple<Args...>& tup);
    
    std::string to_string(std::string str)
    {
        return std::string(str);
    }
    std::string to_string(const char *str)
    {
        return std::string(str);
    }

    template<typename T, size_t N>
    std::string to_string(std::array<T, N> const& arr)
    {
        std::string s="{";
        for (std::size_t t = 0; t != N; ++t)
            s += std::to_string(arr[t]) + (t+1 < N ? ", ":"");
        return s + "}";
    }

    template<typename T>
    std::string to_string(std::vector<T> const& vec)
    {
        std::string s="[";
        for (std::size_t t = 0; t != vec.size(); ++t)
            s += std::to_string(vec[t]) + (t+1 < vec.size() ? ", ":"");
        return s + "]";
    }
    
    // to_string(tuple)
    // https://en.cppreference.com/w/cpp/utility/tuple/operator%3D
    template<class Tuple, std::size_t N>
    struct TupleString
    {
        static std::string str(const Tuple& tup)
        {
            std::string out;
            out += TupleString<Tuple, N-1>::str(tup);
            out += ", ";
            out += std::to_string(std::get<N-1>(tup));
            return out;
        }
    };
    template<class Tuple>
    struct TupleString<Tuple, 1>
    {
        static std::string str(const Tuple& tup)
        {
            std::string out;
            out += std::to_string(std::get<0>(tup));
            return out;
        }
    };
    template<typename... Args>
    std::string to_string(const std::tuple<Args...>& tup)
    {
        std::string out = "(";
        out += TupleString<decltype(tup), sizeof...(Args)>::str(tup);
        out += ")";
        return out;
    }
} // namespace std


/**
 * cout: cout << continer
 */
template <typename T, std::size_t N> // cout << array
std::ostream& operator <<(std::ostream &out, std::array<T, N> &con)
{
    out <<  std::to_string(con);
    return out;
}
template <typename T, typename A> // cout << vector
std::ostream& operator <<(std::ostream &out, std::vector<T, A> &con)
{
    out <<  std::to_string(con);
    return out;
}
template<typename... Args> // cout << tuple
std::ostream& operator <<(std::ostream &out, std::tuple<Args...> &con)
{
    out <<  std::to_string(con);
    return out;
}

/**
 * Concatenate: string << continer
 */
template <class C>
std::string operator <<(std::string str, C &con)
{
    std::string out = str;
    out += std::to_string(con);
    return out;
}
#define STR() std::string("")

int main()
{
    std::array<int, 3> arr {1, 2, 3};
    std::string sArr = std::to_string(arr);
    std::cout << "std::array" << std::endl;
    std::cout << "\ttest to_string: " << sArr << std::endl;
    std::cout << "\ttest cout <<: " << arr << std::endl;
    std::cout << "\ttest string <<: " << (std::string() << arr) << std::endl;
    
    std::vector<std::string> vec {"a", "b"};
    std::string sVec = std::to_string(vec);
    std::cout << "std::vector" << std::endl;
    std::cout << "\ttest to_string: " << sVec << std::endl;
    std::cout << "\ttest cout <<: " << vec << std::endl;
    std::cout << "\ttest string <<: " << (std::string() << vec) << std::endl;
    
    std::tuple<int, std::string> tup = std::make_tuple(5, "five");
    std::string sTup = std::to_string(tup);
    std::cout << "std::tuple" << std::endl;
    std::cout << "\ttest to_string: " << sTup << std::endl;
    std::cout << "\ttest cout <<: " << tup << std::endl;
    std::cout << "\ttest string <<: " << (std::string() << tup) << std::endl;
    
    std::vector<std::tuple<int, float>> vt {std::make_tuple(1, .1), std::make_tuple(2, .2)};
    std::string sVt = std::to_string(vt);
    std::cout << "std::vector<std::tuple>" << std::endl;
    std::cout << "\ttest to_string: " << sVt << std::endl;
    std::cout << "\ttest cout <<: " << vt << std::endl;
    std::cout << "\ttest string <<: " << (std::string() << vt) << std::endl;
    
    std::cout << std::endl;
    
    std::string s1 = "s1: " + std::to_string(arr) + "; " + std::to_string(vec) + "; " + std::to_string(tup);
    std::cout << s1 << std::endl;
    
    std::string s2 = STR() << "s2: " << arr << "; " << vec << "; " << tup;
    std::cout << s2 << std::endl;

    return 0;
}

输出

std::array
    test to_string: {1, 2, 3}
    test cout <<: {1, 2, 3}
    test string <<: {1, 2, 3}
std::vector
    test to_string: [a, b]
    test cout <<: [a, b]
    test string <<: [a, b]
std::tuple
    test to_string: (5, five)
    test cout <<: (5, five)
    test string <<: (5, five)
std::vector<std::tuple>
    test to_string: [(1, 0.100000), (2, 0.200000)]
    test cout <<: [(1, 0.100000), (2, 0.200000)]
    test string <<: [(1, 0.100000), (2, 0.200000)]

s1: {1, 2, 3}; [a, b]; (5, five)
s2: {1, 2, 3}; [a, b]; (5, five)

答案 18 :(得分:0)

如果 boost 是一个选项,那么您可以使用 boost::algorithm::join。例如打印出 std::string 的向量:

#include <boost/algorithm/string/join.hpp>

std::vector<std::string> vs { "some", "string", "vector" };
std::cout << boost::algorithm::join(vs, " | ") << '\n';

对于其他类型的向量,您需要先 transform 字符串

#include <algorithm>
#include <iostream>
#include <numeric>
#include <vector>

#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/transformed.hpp>

int main()
{
    using boost::adaptors::transformed;
    using boost::algorithm::join;

    // Generate the vector
    std::vector<int> vi(10);
    std::iota(vi.begin(), vi.end(), -3);

    // Print out the vector
    std::cout << join(vi |
                 transformed(static_cast<std::string(*)(int)>(std::to_string)),
                 ", ")
              << '\n';
}

Demo on Godbolt

答案 19 :(得分:0)

您可以使用std::experimental::make_ostream_joiner

#include <algorithm>
#include <experimental/iterator>
#include <iostream>
#include <iterator>
#include <numeric>
#include <vector>
 
int main()
{
    std::vector<int> vi(12);
    std::iota(vi.begin(), vi.end(), -5);
    std::cout << "Int vector:\n";
    std::copy(std::begin(vi),
              std::end(vi),
              std::experimental::make_ostream_joiner(std::cout, ", "));

    std::cout <<"\nString vector:\n[";
    std::vector<std::string> vs { "some", "string", "vector" };
    std::copy(std::begin(vs),
              std::end(vs),
              std::experimental::make_ostream_joiner(std::cout, "] - ["));
    std::cout << "]\n";
}

Demo on Godbolt

答案 20 :(得分:-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;

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

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循环可能会更快。