Como faço para iterar mais de cin linha por linha em C ++?

Eu quero iterar std::cin , linha por linha, abordando cada linha como um std::string . Qual é melhor:

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

ou

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

? Qual é a maneira normal de fazer isso?

Desde que o UncleBen trouxe seu LineInputIterator, eu pensei em adicionar mais alguns methods alternativos. Primeiro, uma class muito simples que funciona como um proxy de string:

 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; } }; 

Com isso, você ainda leria usando um istream_iterator normal. Por exemplo, para ler todas as linhas em um arquivo em um vetor de strings, você poderia usar algo como:

 std::vector lines; std::copy(std::istream_iterator(std::cin), std::istream_iterator(), std::back_inserter(lines)); 

O ponto crucial é que quando você está lendo algo, você especifica uma linha – mas, do contrário, você só tem strings.

Outra possibilidade usa uma parte da biblioteca padrão que a maioria das pessoas nem sabe que existe, para não mencionar ser de uso muito real. Quando você lê uma string usando operator >>, o stream retorna uma string de caracteres até o que a localidade do stream diz ser um caractere de espaço em branco. Especialmente se você está fazendo um monte de trabalho que é todo orientado à linha, pode ser conveniente criar uma localidade com uma faceta ctype que apenas classifica a nova linha como espaço em branco:

 struct line_reader: std::ctype { line_reader(): std::ctype(get_table()) {} static std::ctype_base::mask const* get_table() { static std::vector rc(table_size, std::ctype_base::mask()); rc['\n'] = std::ctype_base::space; return &rc[0]; } }; 

Para usá-lo, você impregna o stream que vai ler com uma localidade usando essa faceta, depois apenas lê strings normalmente, e operator >> para uma string sempre lê uma linha inteira. Por exemplo, se quiséssemos ler em linhas e escrever linhas exclusivas em ordem de sorting, poderíamos usar código como este:

 int main() { std::set 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::cin), std::istream_iterator(), std::inserter(lines, lines.end())); std::copy(lines.begin(), lines.end(), std::ostream_iterator(std::cout, "\n")); return 0; } 

Tenha em mente que isso afeta todas as inputs do stream. Usando isso praticamente stream>>my_integer a mistura de input orientada a linha com outra input (por exemplo, ler um número do stream usando o stream>>my_integer normalmente falharia).

O que eu tenho (escrito como um exercício, mas talvez seja útil um dia), é o LineInputIterator:

 #ifndef UB_LINEINPUT_ITERATOR_H #define UB_LINEINPUT_ITERATOR_H #include  #include  #include  #include  namespace ub { template  class LineInputIterator : public std::iterator { public: typedef typename StringT::value_type char_type; typedef typename StringT::traits_type traits_type; typedef std::basic_istream istream_type; LineInputIterator(): is(0) {} LineInputIterator(istream_type& is): is(&is) {} const StringT& operator*() const { return value; } const StringT* operator->() const { return &value; } LineInputIterator& operator++() { assert(is != NULL); if (is && !getline(*is, value)) { is = NULL; } return *this; } LineInputIterator operator++(int) { LineInputIterator prev(*this); ++*this; return prev; } bool operator!=(const LineInputIterator& other) const { return is != other.is; } bool operator==(const LineInputIterator& other) const { return !(*this != other); } private: istream_type* is; StringT value; }; } // end ub #endif 

Então o seu loop pode ser substituído por um algoritmo (outra prática recomendada em C ++):

 for_each(LineInputIterator<>(cin), LineInputIterator<>(), do_stuff); 

Talvez uma tarefa comum seja armazenar todas as linhas em um contêiner:

 vector lines((LineInputIterator<>(stream)), LineInputIterator<>()); 

O primeiro.

Ambos fazem o mesmo, mas o primeiro é muito mais legível, mais você consegue manter a variável string após o loop ser feito (na segunda opção, incluída no escopo do loop for)

Vá com a declaração while.

Veja o Capítulo 16.2 (especificamente páginas 374 e 375) do Code Complete 2 de Steve McConell.

Citar:

Não use um loop for quando um loop while for mais apropriado. Um abuso comum da estrutura de loop flexível em C ++, C # e Java é o conteúdo do loop while em um header de loop for.

.

C ++ Exemplo de um loop while abusivamente Crammed em um for Loop Header

 for (inputFile.MoveToStart(), recordCount = 0; !inputFile.EndOfFile(); recordCount++) { inputFile.GetRecord(); } 

C ++ Exemplo de uso apropriado de um loop while

 inputFile.MoveToStart(); recordCount = 0; while (!InputFile.EndOfFile()) { inputFile.getRecord(); recordCount++; } 

Eu omiti algumas partes no meio, mas espero que isso lhe dê uma boa ideia.