如何在C ++中逐行迭代cin?
我想遍历std::cin
,一行一行地寻址每一行std::string
。 哪个更好:
string line; while (getline(cin, line)) { // process line }
要么
for (string line; getline(cin, line); ) { // process line }
? 什么是正常的方法来做到这一点?
由于UncleBen带来了他的LineInputIterator,我想我会添加更多的替代方法。 首先,一个非常简单的类作为字符串代理:
class line { std::string data; public: friend std::istream &operator>>(std::istream &is, line &l) { std::getline(is, l.data); return is; } operator std::string() const { return data; } };
有了这个,你仍然可以使用正常的istream_iterator来阅读。 例如,要将文件中的所有行读入字符串向量,可以使用如下所示的内容:
std::vector<std::string> lines; std::copy(std::istream_iterator<line>(std::cin), std::istream_iterator<line>(), std::back_inserter(lines));
关键的一点是,当你阅读某些东西的时候,你需要指定一行,否则就只有字符串。
另一种可能性是使用大多数人几乎不知道存在的标准库的一部分,更不用提太多实际的用途。 当使用操作符>>读取一个字符串时,流将返回一串字符,直到该流的语言环境所说的是空格字符。 特别是如果您正在做大量面向行的工作,那么可以方便地创建一个带有ctype方面的语言环境,只将new-line分类为空白:
struct line_reader: std::ctype<char> { line_reader(): std::ctype<char>(get_table()) {} static std::ctype_base::mask const* get_table() { static std::vector<std::ctype_base::mask> rc(table_size, std::ctype_base::mask()); rc['\n'] = std::ctype_base::space; return &rc[0]; } };
为了使用这个,你使用这个方面使用一个语言环境来读取要读取的流,然后通常只读取字符串,操作符>>对于一个字符串来说总是读一整行。 例如,如果我们想要读取行,并按照排序顺序写出独特的行,我们可以使用这样的代码:
int main() { std::set<std::string> lines; // Tell the stream to use our facet, so only '\n' is treated as a space. std::cin.imbue(std::locale(std::locale(), new line_reader())); std::copy(std::istream_iterator<std::string>(std::cin), std::istream_iterator<std::string>(), std::inserter(lines, lines.end())); std::copy(lines.begin(), lines.end(), std::ostream_iterator<std::string>(std::cout, "\n")); return 0; }
请记住,这会影响流中的所有输入。 使用这几乎排除混合面向行的输入与其他输入(例如,使用stream>>my_integer
从流中读取数字通常将失败)。
我所拥有的(写作练习,但也许有一天有用)是LineInputIterator:
#ifndef UB_LINEINPUT_ITERATOR_H #define UB_LINEINPUT_ITERATOR_H #include <iterator> #include <istream> #include <string> #include <cassert> namespace ub { template <class StringT = std::string> class LineInputIterator : public std::iterator<std::input_iterator_tag, StringT, std::ptrdiff_t, const StringT*, const StringT&> { public: typedef typename StringT::value_type char_type; typedef typename StringT::traits_type traits_type; typedef std::basic_istream<char_type, traits_type> istream_type; LineInputIterator(): is(0) {} LineInputIterator(istream_type& is): is(&is) {} const StringT& operator*() const { return value; } const StringT* operator->() const { return &value; } LineInputIterator<StringT>& operator++() { assert(is != NULL); if (is && !getline(*is, value)) { is = NULL; } return *this; } LineInputIterator<StringT> operator++(int) { LineInputIterator<StringT> prev(*this); ++*this; return prev; } bool operator!=(const LineInputIterator<StringT>& other) const { return is != other.is; } bool operator==(const LineInputIterator<StringT>& other) const { return !(*this != other); } private: istream_type* is; StringT value; }; } // end ub #endif
所以你的循环可以被一个算法替换(C ++中另一个推荐的做法):
for_each(LineInputIterator<>(cin), LineInputIterator<>(), do_stuff);
也许一个常见的任务是将每一行存储在一个容器中:
vector<string> lines((LineInputIterator<>(stream)), LineInputIterator<>());
第一个。
两者都是一样的,但是第一个可读性更强,而且在循环完成之后,您可以保留字符串变量(在第二个选项中,它被包含在for循环范围中)
用while语句去吧。
请参阅Steve McConell的Code Complete 2的第16.2章(特别是第374和375页)。
去引用:
while循环更合适时不要使用for循环。 C ++,C#和Java中灵活的for循环结构的常见滥用是随意地将while循环的内容填充到for循环头中。
。
一个while循环的C ++示例滥用压缩成for循环头
for (inputFile.MoveToStart(), recordCount = 0; !inputFile.EndOfFile(); recordCount++) { inputFile.GetRecord(); }
C ++适当使用while循环的例子
inputFile.MoveToStart(); recordCount = 0; while (!InputFile.EndOfFile()) { inputFile.getRecord(); recordCount++; }
我已经省略了中间的一些部分,但希望这给你一个好主意。