cpp*_*ner 32 c++ string line-processing
我想std::cin逐行迭代,将每一行作为a std::string.哪个更好:
string line;
while (getline(cin, line))
{
// process line
}
Run Code Online (Sandbox Code Playgroud)
要么
for (string line; getline(cin, line); )
{
// process line
}
Run Code Online (Sandbox Code Playgroud)
?这样做的正常方法是什么?
Jer*_*fin 72
由于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; }
};
Run Code Online (Sandbox Code Playgroud)
有了这个,你仍然可以使用普通的istream_iterator阅读.例如,要将文件中的所有行读入字符串向量,您可以使用以下内容:
std::vector<std::string> lines;
std::copy(std::istream_iterator<line>(std::cin),
std::istream_iterator<line>(),
std::back_inserter(lines));
Run Code Online (Sandbox Code Playgroud)
关键的一点是,当你正在阅读某些东西时,你指定一条线 - 但除此之外,你只需要字符串.
另一种可能性是使用标准库的一部分,大多数人几乎不知道存在,更不用说具有很多实际用途.当您使用operator >>读取字符串时,该流返回一个字符串,直到该流的区域设置所说的白色空格字符.特别是如果你做了很多面向行的工作,那么用ctype facet创建一个只能将new-line分类为white-space的语言环境会很方便:
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];
}
};
Run Code Online (Sandbox Code Playgroud)
要使用它,您可以使用该方面使用区域设置来填充您要读取的流,然后只是正常读取字符串,而对于字符串,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;
}
Run Code Online (Sandbox Code Playgroud)
请记住,这会影响流中的所有输入.使用这几乎排除了将面向行的输入与其他输入混合(例如,从流中读取数字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
Run Code Online (Sandbox Code Playgroud)
所以你的循环可以用算法替换(C++中另一个推荐的做法):
for_each(LineInputIterator<>(cin), LineInputIterator<>(), do_stuff);
Run Code Online (Sandbox Code Playgroud)
也许一个常见的任务是将每一行存储在一个容器中:
vector<string> lines((LineInputIterator<>(stream)), LineInputIterator<>());
Run Code Online (Sandbox Code Playgroud)
第一个。
两者的作用相同,但第一个更具可读性,而且您可以在循环完成后保留字符串变量(在第二个选项中,它包含在 for 循环范围内)