¿Cómo puedo iterar sobre cin línea por línea en C ++?

Quiero iterar sobre std::cin , línea por línea, direccionando cada línea como std::string . Cual es mejor:

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

o

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

? ¿Cuál es la forma normal de hacer esto?

Como UncleBen trajo su LineInputIterator, pensé que agregaría un par de métodos alternativos. Primero, una clase realmente simple que actúa como un proxy de cadena:

 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 esto, aún leerías usando un istream_iterator normal. Por ejemplo, para leer todas las líneas de un archivo en un vector de cadenas, podría usar algo como:

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

El punto crucial es que cuando estás leyendo algo, especificas una línea, pero de lo contrario, solo tienes cadenas.

Otra posibilidad utiliza una parte de la biblioteca estándar que la mayoría de la gente apenas sabe que existe, sin mencionar que tiene un uso muy real. Cuando lee una cadena con el operador >>, la transmisión devuelve una cadena de caracteres hasta lo que la configuración regional de la secuencia dice que es un carácter de espacio en blanco. Especialmente si está haciendo un montón de trabajo que está orientado a la línea, puede ser conveniente crear una configuración regional con una faceta de tipo c que solo clasifique la nueva línea como espacio en blanco:

 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 usar esto, imbuye la secuencia de la que vas a leer con una configuración regional usando esa faceta, luego solo lee cadenas normalmente, y el operador >> para una cadena siempre lee una línea completa. Por ejemplo, si quisiéramos leer en líneas y escribir líneas únicas en orden ordenado, podríamos usar un 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; } 

Tenga en cuenta que esto afecta todas las entradas de la transmisión. El uso de esto prácticamente descarta mezclar la entrada orientada a la línea con otra entrada (por ejemplo, leer un número de la transmisión usando stream>>my_integer normalmente fallaría).

Lo que tengo (escrito como un ejercicio, pero tal vez resulta útil un día), es 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 

Entonces su bucle podría reemplazarse con un algoritmo (otra práctica recomendada en C ++):

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

Tal vez una tarea común es almacenar cada línea en un contenedor:

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

El primero.

Ambos hacen lo mismo, pero el primero es mucho más legible, además puedes mantener la cadena variable una vez que se completa el ciclo (en la segunda opción, está encerrado en el scope del ciclo for)

Vaya con la statement de while.

Consulte el Capítulo 16.2 (específicamente las páginas 374 y 375) de Code Complete 2 de Steve McConell.

Citar:

No use un bucle for cuando un bucle while sea más apropiado. Un abuso común de la estructura de bucle flexible en C ++, C # y Java es desorganizar el contenido de un bucle while en un encabezado de bucle for.

.

C ++ Ejemplo de un bucle while abusivamente metido en un encabezado de bucle for

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

C ++ Ejemplo de uso apropiado de un ciclo while

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

He omitido algunas partes en el medio, pero espero que eso te dé una buena idea.