2010-02-18 35 views
38

Me gustaría obtener un iterador estilo istream_iterator que devuelve cada línea del archivo como una cadena en lugar de cada palabra. es posible?¿Hay un iterador de C++ que pueda iterar sobre un archivo línea por línea?

+1

Supongo que siempre puedes escribir el tuyo usando la función getline() como dijo Matteo Italia. –

+1

Duplicado: http://stackoverflow.com/questions/1567082/how-do-i-iterate-over-cin-line-by-line-in-c/1567703 –

+3

@Jerry: Ese hilo contiene la respuesta. Pero la pregunta es completamente diferente. – UncleBens

Respuesta

25

EDIT: Este mismo truco que ya fue publicada por otra persona in a previous thread.

Es fácil tener std::istream_iterator hacer lo que quiera:

namespace detail 
{ 
    class Line : std::string 
    { 
     friend std::istream & operator>>(std::istream & is, Line & line) 
     { 
      return std::getline(is, line); 
     } 
    }; 
} 

template<class OutIt> 
void read_lines(std::istream& is, OutIt dest) 
{ 
    typedef std::istream_iterator<detail::Line> InIt; 
    std::copy(InIt(is), InIt(), dest); 
} 

int main() 
{ 
    std::vector<std::string> v; 
    read_lines(std::cin, std::back_inserter(v)); 

    return 0; 
} 
+0

¿Heredar de std :: string aquí no infringe la Regla 35 del Estándar de codificación C++: "Evitar heredar de clases que no fueron diseñadas para ser clases base"? – thehouse

+4

@thehouse - ¿Qué estándar de codificación quiere decir? No creo que haya nada de malo en utilizar una clase arbitraria como base siempre que no se use en un contexto polimórfico. Por ejemplo, el esquema de herencia en mi respuesta sería peligroso si hiciera cosas como 'cadena * ptr = nueva línea; delete ptr; 'pero ese no es el caso aquí – Manuel

+0

@Manuel: Herb Sutter y Andrei Alexandrescu en su libro C++ Coding Standards afirman que" el uso de una clase independiente como base es un grave error de diseño y debe evitarse ". Siguen mencionando cadena explícitamente como una clase mala para heredar. Su argumento gira en torno al hecho de que tienes que hacer una serie de cosas para que la clase base funcione con seguridad y un conjunto de cosas contradictorias para hacer que las clases concretas sean seguras. – thehouse

15

La biblioteca estándar no proporciona iteradores para hacer esto (aunque puede implementar algo así por su cuenta), pero simplemente puede usar getline function (no el método istream) para leer una línea completa de una secuencia de entrada a una cadena de C++.

Ejemplo:

#include <iostream> 
#include <fstream> 
#include <string> 
#include <algorithm> 

using namespace std; 

int main() 
{ 
    ifstream is("test.txt"); 
    string str; 
    while(getline(is, str)) 
    { 
     cout<<str<<endl; 
    } 
    return 0; 
} 
+0

¿Maneja la diferencia en los caracteres eol para las diferentes plataformas (windows/unix/mac)? –

+1

Esa diferencia ya se maneja en el objeto de flujo: cuando abre un archivo en modo texto (el valor predeterminado si no especifica el indicador 'ios :: binary') la secuencia convierte automáticamente el eol específico de la plataforma en' 'simple n'. –

+0

estamos utilizando un COM istream que no trata los EOL de la misma manera. El análisis de dos archivos funcionó pero el análisis de un archivo UNIX (sin LF) provocó que se manejara como si fuera una línea grande. –

4

Se puede escribir su propio iterador. No es tan dificil. Un iterador es solo una clase en la que (hablando simplemente) se definen los operadores increment y *.

Mire http://www.drdobbs.com/cpp/184401417 para comenzar a escribir sus propios iteradores.

+3

@thehouse: es posible que también desee comprobar 'boost :: iterator_facade', que implementa el concepto de iterador completo de STL en términos de algunas funciones básicas. –

1

Puede utilizar istreambuf_iterator en lugar de istream_iterator. No ignora los caracteres de control como istream_iterator.

code.cpp:

#include <iterator> 
#include <iostream> 
#include <fstream> 

using namespace std; 

int main() 
{ 
    ifstream file("input.txt"); 

    istreambuf_iterator<char> i_file(file); 

    istreambuf_iterator<char> eof; 

    std::string buffer; 
    while(i_file != eof) 
    { 
     buffer += *i_file; 
     if(*i_file == '\n') 
     { 
      std::cout << buffer; 
      buffer.clear(); 
     } 
     ++i_file; 
    } 

    return 0; 
} 

input.txt:

ahhhh test *<-- There is a line feed here* 
bhhhh second test *<-- There is a line feed here* 

salida:

ahhhh test 
bhhhh second test 
5

Aquí es una solución. El ejemplo imprime el archivo de entrada con @@ al final de cada línea.

#include <iostream> 
#include <iterator> 
#include <fstream> 
#include <string> 

using namespace std; 

class line : public string {}; 

std::istream &operator>>(std::istream &is, line &l) 
{ 
    std::getline(is, l); 
    return is; 
} 

int main() 
{ 
    std::ifstream inputFile("input.txt"); 

    istream_iterator<line> begin(inputFile); 
    istream_iterator<line> end; 

    for(istream_iterator<line> it = begin; it != end; ++it) 
    { 
     cout << *it << "@@\n"; 
    } 

    getchar(); 
} 

Editar: Manuel ha sido más rápido.

+0

@Rexxar - Es malo, lo borré y luego lo borré como wiki de la comunidad. Lo siento por eso. – Manuel

Cuestiones relacionadas