当前位置: 首页 > 知识库问答 >
问题:

如何打印出矢量的内容?

太叔望
2023-03-14

如何将std::vector的内容打印到屏幕?

实现以下< code >运算符的解决方案

template<container C, class T, String delim = ", ", String open = "[", String close = "]">
std::ostream & operator<<(std::ostream & o, const C<T> & x)
{
  // ... What can I write here?
}

以下是我目前所拥有的,没有单独的功能:

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

共有3个答案

邰建业
2023-03-14

此解决方案的灵感来自 Marcelo 的解决方案,但进行了一些更改:

#include <iostream>
#include <iterator>
#include <type_traits>
#include <vector>
#include <algorithm>

// This works similar to ostream_iterator, but doesn't print a delimiter after the final item
template<typename T, typename TChar = char, typename TCharTraits = std::char_traits<TChar> >
class pretty_ostream_iterator : public std::iterator<std::output_iterator_tag, void, void, void, void>
{
public:
    typedef TChar char_type;
    typedef TCharTraits traits_type;
    typedef std::basic_ostream<TChar, TCharTraits> ostream_type;

    pretty_ostream_iterator(ostream_type &stream, const char_type *delim = NULL)
        : _stream(&stream), _delim(delim), _insertDelim(false)
    {
    }

    pretty_ostream_iterator<T, TChar, TCharTraits>& operator=(const T &value)
    {
        if( _delim != NULL )
        {
            // Don't insert a delimiter if this is the first time the function is called
            if( _insertDelim )
                (*_stream) << _delim;
            else
                _insertDelim = true;
        }
        (*_stream) << value;
        return *this;
    }

    pretty_ostream_iterator<T, TChar, TCharTraits>& operator*()
    {
        return *this;
    }

    pretty_ostream_iterator<T, TChar, TCharTraits>& operator++()
    {
        return *this;
    }

    pretty_ostream_iterator<T, TChar, TCharTraits>& operator++(int)
    {
        return *this;
    }
private:
    ostream_type *_stream;
    const char_type *_delim;
    bool _insertDelim;
};

#if _MSC_VER >= 1400

// Declare pretty_ostream_iterator as checked
template<typename T, typename TChar, typename TCharTraits>
struct std::_Is_checked_helper<pretty_ostream_iterator<T, TChar, TCharTraits> > : public std::tr1::true_type
{
};

#endif // _MSC_VER >= 1400

namespace std
{
    // Pre-declarations of container types so we don't actually have to include the relevant headers if not needed, speeding up compilation time.
    // These aren't necessary if you do actually include the headers.
    template<typename T, typename TAllocator> class vector;
    template<typename T, typename TAllocator> class list;
    template<typename T, typename TTraits, typename TAllocator> class set;
    template<typename TKey, typename TValue, typename TTraits, typename TAllocator> class map;
}

// Basic is_container template; specialize to derive from std::true_type for all desired container types
template<typename T> struct is_container : public std::false_type { };

// Mark vector as a container
template<typename T, typename TAllocator> struct is_container<std::vector<T, TAllocator> > : public std::true_type { };

// Mark list as a container
template<typename T, typename TAllocator> struct is_container<std::list<T, TAllocator> > : public std::true_type { };

// Mark set as a container
template<typename T, typename TTraits, typename TAllocator> struct is_container<std::set<T, TTraits, TAllocator> > : public std::true_type { };

// Mark map as a container
template<typename TKey, typename TValue, typename TTraits, typename TAllocator> struct is_container<std::map<TKey, TValue, TTraits, TAllocator> > : public std::true_type { };

// Holds the delimiter values for a specific character type
template<typename TChar>
struct delimiters_values
{
    typedef TChar char_type;
    const TChar *prefix;
    const TChar *delimiter;
    const TChar *postfix;
};

// Defines the delimiter values for a specific container and character type
template<typename T, typename TChar>
struct delimiters
{
    static const delimiters_values<TChar> values; 
};

// Default delimiters
template<typename T> struct delimiters<T, char> { static const delimiters_values<char> values; };
template<typename T> const delimiters_values<char> delimiters<T, char>::values = { "{ ", ", ", " }" };
template<typename T> struct delimiters<T, wchar_t> { static const delimiters_values<wchar_t> values; };
template<typename T> const delimiters_values<wchar_t> delimiters<T, wchar_t>::values = { L"{ ", L", ", L" }" };

// Delimiters for set
template<typename T, typename TTraits, typename TAllocator> struct delimiters<std::set<T, TTraits, TAllocator>, char> { static const delimiters_values<char> values; };
template<typename T, typename TTraits, typename TAllocator> const delimiters_values<char> delimiters<std::set<T, TTraits, TAllocator>, char>::values = { "[ ", ", ", " ]" };
template<typename T, typename TTraits, typename TAllocator> struct delimiters<std::set<T, TTraits, TAllocator>, wchar_t> { static const delimiters_values<wchar_t> values; };
template<typename T, typename TTraits, typename TAllocator> const delimiters_values<wchar_t> delimiters<std::set<T, TTraits, TAllocator>, wchar_t>::values = { L"[ ", L", ", L" ]" };

// Delimiters for pair
template<typename T1, typename T2> struct delimiters<std::pair<T1, T2>, char> { static const delimiters_values<char> values; };
template<typename T1, typename T2> const delimiters_values<char> delimiters<std::pair<T1, T2>, char>::values = { "(", ", ", ")" };
template<typename T1, typename T2> struct delimiters<std::pair<T1, T2>, wchar_t> { static const delimiters_values<wchar_t> values; };
template<typename T1, typename T2> const delimiters_values<wchar_t> delimiters<std::pair<T1, T2>, wchar_t>::values = { L"(", L", ", L")" };

// Functor to print containers. You can use this directly if you want to specificy a non-default delimiters type.
template<typename T, typename TChar = char, typename TCharTraits = std::char_traits<TChar>, typename TDelimiters = delimiters<T, TChar> >
struct print_container_helper
{
    typedef TChar char_type;
    typedef TDelimiters delimiters_type;
    typedef std::basic_ostream<TChar, TCharTraits>& ostream_type;

    print_container_helper(const T &container)
        : _container(&container)
    {
    }

    void operator()(ostream_type &stream) const
    {
        if( delimiters_type::values.prefix != NULL )
            stream << delimiters_type::values.prefix;
        std::copy(_container->begin(), _container->end(), pretty_ostream_iterator<typename T::value_type, TChar, TCharTraits>(stream, delimiters_type::values.delimiter));
        if( delimiters_type::values.postfix != NULL )
            stream << delimiters_type::values.postfix;
    }
private:
    const T *_container;
};

// Prints a print_container_helper to the specified stream.
template<typename T, typename TChar, typename TCharTraits, typename TDelimiters>
std::basic_ostream<TChar, TCharTraits>& operator<<(std::basic_ostream<TChar, TCharTraits> &stream, const print_container_helper<T, TChar, TDelimiters> &helper)
{
    helper(stream);
    return stream;
}

// Prints a container to the stream using default delimiters
template<typename T, typename TChar, typename TCharTraits>
typename std::enable_if<is_container<T>::value, std::basic_ostream<TChar, TCharTraits>&>::type
    operator<<(std::basic_ostream<TChar, TCharTraits> &stream, const T &container)
{
    stream << print_container_helper<T, TChar, TCharTraits>(container);
    return stream;
}

// Prints a pair to the stream using delimiters from delimiters<std::pair<T1, T2>>.
template<typename T1, typename T2, typename TChar, typename TCharTraits>
std::basic_ostream<TChar, TCharTraits>& operator<<(std::basic_ostream<TChar, TCharTraits> &stream, const std::pair<T1, T2> &value)
{
    if( delimiters<std::pair<T1, T2>, TChar>::values.prefix != NULL )
        stream << delimiters<std::pair<T1, T2>, TChar>::values.prefix;

    stream << value.first;

    if( delimiters<std::pair<T1, T2>, TChar>::values.delimiter != NULL )
        stream << delimiters<std::pair<T1, T2>, TChar>::values.delimiter;

    stream << value.second;

    if( delimiters<std::pair<T1, T2>, TChar>::values.postfix != NULL )
        stream << delimiters<std::pair<T1, T2>, TChar>::values.postfix;
    return stream;    
}

// Used by the sample below to generate some values
struct fibonacci
{
    fibonacci() : f1(0), f2(1) { }
    int operator()()
    {
        int r = f1 + f2;
        f1 = f2;
        f2 = r;
        return f1;
    }
private:
    int f1;
    int f2;
};

int main()
{
    std::vector<int> v;
    std::generate_n(std::back_inserter(v), 10, fibonacci());

    std::cout << v << std::endl;

    // Example of using pretty_ostream_iterator directly
    std::generate_n(pretty_ostream_iterator<int>(std::cout, ";"), 20, fibonacci());
    std::cout << std::endl;
}

与Marcelo的版本一样,它使用了一个is_container类型的特性,该特性必须专门用于所有要支持的容器。可能可以使用特征来检查value_typeconst_iteratorbegin()/end(),但我不确定我是否建议这样做,因为它可能匹配符合这些条件但实际上不是容器的东西,如

主要区别在于我围绕pretty_ostream_iterator构建了我的版本,它的工作原理类似于 std::ostream_iterator,但不会在最后一项之后打印分隔符。容器的格式设置由print_container_helper完成,可以直接用于打印没有is_container特征的容器,或指定不同的分隔符类型。

我还定义了is_container和分隔符,因此它适用于具有非标准谓词或分配器的容器,以及char和wchar_t。运算符

最后,我使用了 std::enable_if,它作为 C 0x 的一部分提供,适用于 Visual C 2010 和 g 4.3(需要 -std=c 0x 标志)及更高版本。这样就没有对 Boost 的依赖。

柴丰
2023-03-14

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

#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是所谓的迭代器适配器。它被模板化到要打印到流的类型上(在本例中,<code>char</code>)cout(又名控制台输出)是我们要写入的流,而空格字符(“”)是我们希望在向量中存储的每个元素之间打印的内容。

这个标准算法是强大的,许多其他算法也是如此。标准库给你的力量和灵活性是它如此伟大的原因。想象一下:你只需一行代码就可以将向量打印到控制台。你不必处理分隔符字符的特殊情况。你不需要担心for循环。标准库为你做了一切。

黄靖
2023-03-14

如果你有一个 C 11 编译器,我建议使用基于范围的 for 循环(见下文);或者使用迭代器。但是您有几种选择,我将在下面解释所有这些选择。

在 C 11(及更高版本)中,您可以使用新的基于范围的 for 循环,如下所示:

std::vector<char> path;
// ...
for (char i: path)
    std::cout << i << ' ';

for-loop语句中的< code>char类型应该是vector path的元素类型,而不是整数索引类型。换句话说,由于< code>path属于< code>std::vector类型

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

无论您是使用显式类型还是使用auto关键字,对象i都具有一个值,该值是路径object中实际项的副本。因此,循环中对i的所有更改都不会保留在路径自身中:

std::vector<char> path{'a', 'b', 'c'};

for (auto i: path) {
    i = '_'; // 'i' is a copy of the element in 'path', so although
             // we can change 'i' here perfectly fine, the elements
             // of 'path' have not changed
    std::cout << i << ' '; // will print: "_ _ _"
}

for (auto i: path) {
    std::cout << i << ' '; // will print: "a b c"
}

如果您也想禁止在for循环中更改i的复制值,您可以强制i的类型为const char,如下所示:

for (const auto i: path) {
    i = '_'; // this will now produce a compiler error
    std::cout << i << ' ';
}

如果您想修改path中的项目,以便这些更改保留在for循环之外的path中,那么您可以使用如下引用:

for (auto& i: path) {
    i = '_'; // changes to 'i' will now also change the
             // element in 'path' itself to that value
    std::cout << i << ' ';
}

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

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

在C 11之前,规范的解决方案是使用迭代器,这仍然是完全可以接受的。它们的用法如下:

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

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

这不是另一个解决方案,而是对上述迭代器解决方案的补充。如果您使用的是 C 11 标准(或更高版本),则可以使用 auto 关键字来帮助提高可读性:

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

这里 i 的类型将是非常量(即编译器将使用 std::vector

for (auto i = path.cbegin(); i != path.cend(); ++i) {
    *i = '_'; // will produce a compiler error
    std::cout << *i << ' ';
}

如果你对编译器推断类型不满意,那么在 C 11 中,你可以使用类型别名来避免一直输入向量(一个好习惯):

using Path = std::vector<char>; // C++11 onwards only
Path path; // 'Path' is an alias for std::vector<char>
// ...
for (Path::const_iterator i = path.begin(); i != path.end(); ++i)
    std::cout << *i << ' ';

如果您无权访问C 11编译器(或者出于任何原因不喜欢类型别名语法),那么您可以使用更传统的typedef

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

在这一点上,您可能会或可能不会遇到迭代器,您可能或可能没有听说迭代器是您“应该”使用的,可能会想知道为什么。答案并不容易理解,但简而言之,其思想是迭代器是一种抽象,它可以保护您不受操作细节的影响。

让一个对象(迭代器)来执行您想要的操作(比如顺序访问)比您自己编写细节更方便(“细节”是实际访问向量元素的代码)。您应该注意到,在for循环中,您只要求迭代器返回一个值(< code>*i,其中< code>i是迭代器),而不是直接与< code>path本身进行交互。逻辑是这样的:你创建一个迭代器,给它你想要循环的对象(< code > iterator I = path . begin()),然后你要做的就是让迭代器为你获取下一个值(< code > * I );你从来不需要担心迭代器是如何做到的——那是它的事,与你无关。

好的,但是有什么意义呢?好吧,想象一下,如果获得一个值并不简单。如果它涉及一些工作怎么办?你不需要担心,因为迭代器已经为你处理了这个问题——它会整理细节,你需要做的就是向它询问一个值。此外,如果将容器从 std::vector 更改为其他内容怎么办?从理论上讲,即使访问新容器中的元素的细节如何更改,您的代码也不会更改:请记住,迭代器在幕后为您整理所有详细信息,因此您根本不需要更改代码 - 您只需向迭代器询问容器中的下一个值,与以前相同。

因此,虽然这似乎与循环向量混淆了矫枉过正,但迭代器的概念背后有充分的理由,因此您不妨习惯使用它们。

您还可以使用整数类型显式索引for循环中的vector元素:

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

如果要这样做,最好使用容器的成员类型(如果它们可用且合适)std::vector有一个名为size_type的成员类型:它是size方法返回的类型。

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

为什么不优先使用< code>iterator解决方案呢?对于简单的情况,您可以这样做,但是使用< code >迭代器有几个好处,我在上面已经简要介绍过了。因此,我的建议是避免这种方法,除非你有充分的理由。

看看约书亚的回答。您可以使用STL算法std::copy将向量内容复制到输出流中。我没有什么要补充的,除了说我不使用这种方法;但除了习惯之外,没有什么好的理由。

为了完整性,C20引入了范围,它可以作用于<code>std::vector</code>的整个范围,因此不需要<code>begin</ccode>和<code>end</code>:

#include <iterator> // for std::ostream_iterator
#include <algorithm> // for std::ranges::copy depending on lib support

std::vector<char> path;
// ...
std::ranges::copy(path, std::ostream_iterator<char>(std::cout, " "));

除非您有最新的编译器(在GCC上显然至少是10.1版),否则即使您可能有一些C 20功能可用,您也可能没有范围支持。

另请参阅下面的克里斯的回答。这更像是对其他答案的补充,因为您仍然需要在重载中实现上述解决方案之一,但好处是代码更干净。这就是使用上面的 std::ranges::copy 解决方案的方式:

#include <iostream>
#include <vector>
#include <iterator> // for std::ostream_iterator
#include <algorithm> // for std::ranges::copy depending on lib support

using Path = std::vector<char>; // type alias for std::vector<char>

std::ostream& operator<< (std::ostream& out, const Path& v) {
    if ( !v.empty() ) {
        out << '[';
        std::ranges::copy(v, std::ostream_iterator<char>(out, ", "));
        out << "\b\b]"; // use two ANSI backspace characters '\b' to overwrite final ", "
    }
    return out;
}

int main() {
    Path path{'/', 'f', 'o', 'o'};

    // will output: "path: [/, f, o, o]"
    std::cout << "path: " << path << std::endl;

    return 0;
}

现在您可以像基本类型一样将Path对象传递到您的输出流。使用上面的任何其他解决方案也应该同样简单。

这里提出的任何解决方案都将奏效。这取决于你(以及上下文或你的编码标准)哪个是“最好的”。比这更详细的东西可能最好留给另一个问题,在这个问题上可以正确评估利弊,但一如既往,用户偏好将始终发挥作用:所提出的解决方案没有一个客观上是错误的,但有些对每个编码人员来说会更好。

这是我之前发布的一个扩展解决方案。由于这篇帖子不断受到关注,我决定对其进行扩展,并参考这里发布的其他优秀解决方案,至少那些我个人在过去至少使用过一次的解决方案。然而,我鼓励读者看看下面的答案,因为可能有一些好的建议我已经忘记或不知道了。

 类似资料:
  • 我正在尝试感受 C ,我想打印向量的内容,或者,为了确认我的程序是正确的,我可以添加向量的内容并打印结果。 这是我的代码: 我在网上找到了一些打印矢量的解决方案,但是我不明白这些代码在做什么,所以我希望有人能帮忙。

  • 错误信息: D:\main.cpp:91:错误:与“operator<<”不匹配(操作数类型为“std::ostream{aka std::basic_ostream}”和“__gnu_cxx::__alloc_traits>::value_type{aka Obj}”)cout<

  • 我正在使用push_back()函数插入这些元素,我想知道 我如何打印向量c的内容? 我尝试过

  • 我试图使用“cout”将一个整数向量打印到终端,但是在编译过程中我得到一条错误消息: 没有“运算符”的匹配项 代码片段如下所示: 给向量赋值没有错误,只有代码的“cout”部分出错

  • 问题内容: 我想在引号中打印一个变量。我想打印出来 我已经尝试了很多,但有效的方法是: –但是输出中有两个空格-> 当我不使用逗号时,会出现语法错误。 如何在一对引号内打印内容? 问题答案: 您可以使用: 在这里了解格式:格式 在3x中,您可以使用:

  • 我正在尝试将集合的内容打印到Spark控制台。 我有一个类型: 我使用命令: 但这是打印的: res1:组织。阿帕奇。火花rdd。RDD[单位]=MappedRDD[4]位于map at:19 如何将RDD写入控制台或保存到磁盘,以便查看其内容?