我想逐行迭代std::cin
,将每一行表示为std::string
。哪种方法更好?
string line;
while (getline(cin, line))
{
// process line
}
或者for (string line; getline(cin, line); )
{
// process line
}
正常的做法是什么?
我想逐行迭代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));
关键点是当您读取时,指定一行。否则,你只有字符串。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];
}
};
使用这个功能,您需要用该facet为将要从中读取的流注入locale,然后正常地读取字符串,对于字符串的operator>>读取整行。例如,如果我们想要读取行,并按排序顺序写出唯一的行,我们可以使用以下代码:
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
for_each(LineInputIterator<>(cin), LineInputIterator<>(), do_stuff);
vector<string> lines((LineInputIterator<>(stream)), LineInputIterator<>());
第一个。
两者都可以实现相同功能,但第一个更易读,并且在循环结束后您可以继续使用字符串变量(在第二个选项中,它被限制在for循环范围内)。
{ string line; while (getline(cin,line)) { // process line } } // more code goes here
。 - Steve Jessopstd::ranges::istream_view
。我还为类添加了行号。我在godbolt上完成了这个过程,以便查看发生了什么。这个版本的line类仍然可以与std::input_iterator
一起使用。
https://en.cppreference.com/w/cpp/ranges/basic_istream_view
https://www.godbolt.org/z/94Khjz
class line {
std::string data{};
std::intmax_t line_number{-1};
public:
friend std::istream &operator>>(std::istream &is, line &l) {
std::getline(is, l.data);
++l.line_number;
return is;
}
explicit operator std::string() const { return data; }
explicit operator std::string_view() const noexcept { return data; }
constexpr explicit operator std::intmax_t() const noexcept { return line_number; }
};
int main()
{
std::string l("a\nb\nc\nd\ne\nf\ng");
std::stringstream ss(l);
for(const auto & x : std::ranges::istream_view<line>(ss))
{
std::cout << std::intmax_t(x) << " " << std::string_view(x) << std::endl;
}
}
输出:
0 a
1 b
2 c
3 d
4 e
5 f
6 g
使用while语句。
请参阅Steve McConell的《代码大全2》第16.2章(特别是374和375页)。
引用如下:
当while循环更合适时,不要使用for循环。在C++、C#和Java中,对灵活的for循环结构的常见滥用是将while循环的内容随意塞入for循环头中。
.
C++中while循环被滥用地塞进了for循环头的示例for (inputFile.MoveToStart(), recordCount = 0; !inputFile.EndOfFile(); recordCount++) {
inputFile.GetRecord();
}
C++使用while循环的适当示例
inputFile.MoveToStart();
recordCount = 0;
while (!InputFile.EndOfFile()) {
inputFile.getRecord();
recordCount++;
}
我省略了一些中间部分,但希望这能给你一个好的想法。
for
循环,他认为这个稍微比你描述的更好。 - Jonathan Fingland
operator std::string()
改成 const:https://ideone.com/xcJ4Z vs https://ideone.com/YY8cQ - Cubbi