如何将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;
}
}
此解决方案的灵感来自 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_type
、const_iterator
、begin()
/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 的依赖。
一个更简单的方法是使用标准复制算法:
#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循环。标准库为你做了一切。
如果你有一个 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写入控制台或保存到磁盘,以便查看其内容?