Come faccio a scorrere su cin una riga dopo l’altra in C ++?

Voglio ripetere std::cin , riga per riga, indirizzando ogni riga come una std::string . Che è migliore:

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

o

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

? Qual è il modo normale per farlo?

Da quando UncleBen ha presentato il suo LineInputIterator, ho pensato di aggiungere un paio di altri metodi alternativi. Innanzitutto, una class davvero semplice che funge da proxy delle stringhe:

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

Con questo, continueresti a leggere usando un normale istream_iterator. Ad esempio, per leggere tutte le righe di un file in un vettore di stringhe, puoi usare qualcosa come:

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

Il punto cruciale è che quando stai leggendo qualcosa, specifichi una linea – ma altrimenti hai solo stringhe.

Un’altra possibilità utilizza una parte della biblioteca standard che la maggior parte delle persone conosce a mala pena, per non parlare dell’uso molto reale. Quando leggi una stringa usando l’operatore >>, lo stream restituisce una stringa di caratteri fino a qualsiasi cosa che il locale del stream dice sia un carattere di spazio bianco. Soprattutto se stai facendo un sacco di lavoro che è tutto orientato alla linea, può essere conveniente creare una localizzazione con una faccetta di tipo ctype che classifica solo new-line come white-space:

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

Per utilizzarlo, si impregna lo stream da cui si leggerà con le impostazioni internazionali utilizzando tale facet, quindi si leggono semplicemente le stringhe normalmente e l’operatore >> per una stringa legge sempre un’intera riga. Ad esempio, se volessimo leggere le righe e scrivere righe univoche in ordine, potremmo usare un codice come questo:

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

Tieni presente che ciò influisce su tutti gli input dallo stream. Usando questa regola si mescolano gli input orientati alla linea con altri input (ad esempio leggendo un numero dallo stream usando lo stream>>my_integer normalmente fallisce).

Quello che ho (scritto come esercizio, ma forse risulta utile un giorno), è 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 

Quindi il tuo ciclo potrebbe essere sostituito con un algoritmo (un’altra pratica raccomandata in C ++):

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

Forse un compito comune è quello di memorizzare ogni riga in un contenitore:

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

Il primo.

Entrambi fanno lo stesso, ma il primo è molto più leggibile, in più si ottiene di mantenere la variabile di stringa dopo che il ciclo è terminato (nella seconda opzione, inclusa nell’oscilloscopio for loop)

Vai con la dichiarazione while.

Si veda il Capitolo 16.2 (in particolare le pagine 374 e 375) di Code Complete 2 di Steve McConell.

Per citare:

Non utilizzare un ciclo for quando un ciclo while è più appropriato. Un abuso comune della struttura flessibile per la struttura del ciclo in C ++, C # e Java è a casaccio che racchiude il contenuto di un ciclo while in un’intestazione di ciclo for.

.

C ++ Esempio di loop while abusivamente Crammed in a per Loop Header

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

C ++ Esempio di uso appropriato di un ciclo while

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

Ho omesso alcune parti nel mezzo, ma spero che ti dia una buona idea.