C++ 如何在C+中逐行迭代cin+;?

C++ 如何在C+中逐行迭代cin+;?,c++,string,line-processing,C++,String,Line Processing,我想逐行迭代std::cin,将每一行寻址为std::string。哪一个更好: string line; while (getline(cin, line)) { // process line } 或 ??通常的做法是什么?第一种 两者的作用相同,但第一个更具可读性,而且在循环完成后可以保留字符串变量(在第二个选项中,它包含在for循环范围内)使用while语句 参见史蒂夫·麦科内尔的《代码完成2》第16.2章(特别是第374页和第375页) 引述: 在while循环更适合时不要

我想逐行迭代
std::cin
,将每一行寻址为
std::string
。哪一个更好:

string line;
while (getline(cin, line))
{
    // process line
}

??通常的做法是什么?

第一种


两者的作用相同,但第一个更具可读性,而且在循环完成后可以保留字符串变量(在第二个选项中,它包含在for循环范围内)

使用while语句

参见史蒂夫·麦科内尔的《代码完成2》第16.2章(特别是第374页和第375页)

引述:

<强>在while循环更适合时不要使用for循环。< /强> C++中的灵活循环结构,C语言和java通常滥用while循环的内容到for循环报头。< /P>

C++将while循环滥用地塞进for循环头的示例

C++适当使用while循环的示例

我省略了中间部分,但希望这能给你一个好主意。

< P>我所写的(作为一个练习,但也许有一天是有用的),是线性函数:

#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
\ifndef UB\u LINEINPUT\u迭代器
#定义UB_LINEINPUT_迭代器
#包括
#包括
#包括
#包括
命名空间ub{
模板
类LineInputIterator:
公共标准:迭代器
{
公众:
typedef typename StringT::value_type char_type;
typedef typename StringT::traits\u type traits\u type;
typedef std::基本istream istream类型;
LineInputIterator():是(0){}
lineinputierator(istream_type&is):is(&is){
常量StringT&运算符*()常量{返回值;}
常量StringT*运算符->()常量{return&value;}
LineInputIterator和运算符++()
{
断言(is!=NULL);
if(is&!getline(*is,value)){
is=NULL;
}
归还*这个;
}
LineInputIterator运算符++(int)
{
lineinputierator prev(*此项);
++*这,;
返回上一个;
}
布尔运算符!=(常量行输入计算器和其他)常量
{
return is!=other.is;
}
布尔运算符==(常量行输入计算器和其他)常量
{
返回!(*此!=其他);
}
私人:
istream_类型*为;
StringT值;
};
}//结束ub
#恩迪夫
因此,可以用一种算法(C++中的另一种推荐做法)替换您的循环:

对于每个(LineInputIterator(cin)、LineInputIterator()、do_stuff);
也许一个常见的任务是将每一行存储在一个容器中:

vector<string> lines((LineInputIterator<>(stream)), LineInputIterator<>());
向量行((LineInputIterator(stream)),LineInputIterator();

由于UncleBen提出了他的LineInputator,我想我应该添加更多的替代方法。首先,一个非常简单的类充当字符串代理:

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_迭代器进行读取。例如,要将文件中的所有行读入字符串向量,可以使用以下方法:

std::vector<std::string> lines;

std::copy(std::istream_iterator<line>(std::cin), 
          std::istream_iterator<line>(),
          std::back_inserter(lines));
std::矢量线;
std::copy(std::istream_迭代器(std::cin),
std::istream_迭代器(),
标准:背面插入器(线);
关键的一点是,当你读东西的时候,你需要指定一行,否则,你就只有字符串了

另一种可能是使用大多数人几乎不知道存在的标准库的一部分,更不用说有很多实际用途了。当您使用操作符>>读取字符串时,流将返回一个字符串,该字符串的区域设置表示为空白字符。特别是如果您正在进行大量面向行的工作,则可以方便地使用ctype方面创建区域设置,该方面仅将新行分类为空白:

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];
    }
};  
struct line\u读取器:std::ctype{
行_reader():std::ctype(get_table()){}
静态std::ctype_base::mask const*get_table(){
静态std::vector
rc(表大小,std::ctype_base::mask());
rc['\n']=std::ctype_base::space;
返回&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;
}
intmain(){
std::设置行;
//告诉流使用我们的方面,因此只有“\n”被视为空间。
std::cin.imbue(std::locale(std::locale(),new line_reader());
std::copy(std::istream_迭代器(std::cin),
std::istream_迭代器(),
std::inserter(lines,lines.end());
std::copy(lines.begin(),lines.end(),
std::ostream_迭代器(std::cout,“\n”);
返回0;
}

请记住,这会影响流中的所有输入。使用此选项几乎可以避免将面向行的输入与其他输入混合(例如,使用
流>>my_integer
从流中读取数字通常会失败)。

这是基于Jerry Coffin的答案。我想展示c++20的
std::ranges::istream\u视图
。我还向类添加了一个行号。我在godbolt上做了这件事,所以我可以看到发生了什么。这个版本的line类仍然适用于
std::input\u迭代器

类行{
std::字符串数据{};
std::intmax\u t行数{-1};
公众:
friend std::istream&operator>>(std::istream&is,line&l){
std::getline(is,l.data);
++l、 线号;
回报是;
}
显式运算符std::string()常量{返回数据;}
显式运算符std::string_view()const noexcept{return data;}
constexpr显式运算符std::intmax_t()const noexcept{return line_number;}
};
int main()
{
std::字符串l(“a\nb\nc\nd\ne\nf\ng”);
std::stringstream ss(l);
用于(常量自动&x:std::范围::istream_视图(ss))
{

将线路保持在范围内不是件好事吗
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; }    
};
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];
    }
};  
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;
}
0 a
1 b
2 c
3 d
4 e
5 f
6 g