2010-02-18 10 views
38

Chciałbym uzyskać iterator w stylu istream_iterator, który zwraca każdą linię pliku jako ciąg, a nie każde słowo. czy to możliwe?Czy istnieje iterator C++, który może iterować po pliku wiersz po linii?

+1

Myślę, że zawsze możesz napisać własną korzystając z funkcji getline(), jak powiedział Matteo Italia. –

+1

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

+3

@Jerry: Wątek zawiera odpowiedź. Ale pytanie jest zupełnie inne. – UncleBens

Odpowiedz

25

EDIT: Ta sama sztuczka została już wysłana przez kogoś innego in a previous thread.

Łatwo jest mieć std::istream_iterator rób co chcesz:

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

Dziedziczenie z std :: string nie narusza tutaj reguły 35 standardu kodowania C++: "Unikaj dziedziczenia z klas, które nie zostały zaprojektowane jako klasy podstawowe"? – thehouse

+4

@thehouse - Jaki standard kodowania masz na myśli? Nie sądzę, że jest coś złego w używaniu arbitralnej klasy jako bazy pod warunkiem, że nie jest używana w kontekście polimorficznym. Na przykład schemat dziedziczenia w mojej odpowiedzi byłby niebezpieczny, gdybym zrobił takie rzeczy jak 'string * ptr = new Line; delete ptr; 'ale nie w tym przypadku. – Manuel

+0

@Manuel: Herb Sutter i Andrei Alexandrescu w swojej książce Standardy kodowania C++ stwierdzają, że" używanie samodzielnej klasy jako podstawy jest poważnym błędem konstrukcyjnym i należy go unikać ". Dodają, że ciąg jest jawnie zwany klasą złą dziedziczoną. Ich argumenty odnoszą się do faktu, że musisz wykonać jeden zestaw rzeczy, aby klasa bazowa mogła bezpiecznie pracować, oraz zestaw sprzecznych rzeczy, które zapewnią bezpieczeństwo klasom. – thehouse

15

W standardowej bibliotece nie jest to możliwe (choć można to zaimplementować samodzielnie), ale można po prostu użyć getline function (a nie metody istream), aby odczytać całą linię ze strumienia wejściowego do ciąg C++.

Przykład:

#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

Czy obsługuje różnicę w znakach eol dla różnych platform (Windows/Unix/Mac)? –

+1

Ta różnica jest już obsługiwana w obiekcie strumienia: gdy otwierasz plik w trybie tekstowym (domyślny, jeśli nie określisz flagi 'ios :: binary'), strumień automatycznie konwertuje specyficzny dla platformy eol na zwykły' \ n'. –

+0

używamy komputera, który nie traktował EOL-ów tak samo. Analizowanie pliku DOS działało, ale parsowanie pliku UNIX (bez LF) powodowało, że był on obsługiwany tak, jakby był jedną dużą linią. –

4

można napisać własny iterator. To nie jest takie trudne. Iterator jest po prostu klasą, w której (po prostu mówiąc) przyrost i * operatory są zdefiniowane.

Spójrz na http://www.drdobbs.com/cpp/184401417, aby rozpocząć pisanie własnych iteratorów.

+3

@thehouse: możesz również chcieć sprawdzić 'boost :: iterator_facade', które implementuje pełną koncepcję iteratora STL pod względem kilku podstawowych funkcji. –

1

Możesz użyć istreambuf_iterator zamiast istream_iterator. Nie ignoruje znaków sterujących, takich jak 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* 

Wydajność:

ahhhh test 
bhhhh second test 
5

tu rozwiązanie. Przykład wypisuje plik wejściowy z @@ na końcu każdej linii.

#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(); 
} 

Edit: Manuel była szybsza.

+0

@Rexxar - Mój zły, usunąłem go, a następnie skasowałem jako wiki społeczności. Przepraszam za to. – Manuel

Powiązane problemy