2013-11-21 15 views
5

This answer zwraca uwagę na fakt, że C++ nie nadaje się dobrze do iteracji pliku binarnego, ale tego właśnie potrzebuję, krótko mówiąc, operuję na plikach w "binarny" sposób, tak, wszystkie pliki są binarne nawet pliki .txt, ale piszę coś, co działa na plikach obrazów, więc muszę czytać pliki, które są dobrze skonstruowane, gdyby dane były ułożone w określony sposób.Pliki binarne i iteratory C++: ucieczka z 1: 1 przy użyciu ifstreambuf_iterator?

Chciałbym przeczytać cały plik w strukturze danych, takiej jak std::vector<T>, dzięki czemu mogę prawie natychmiast zamknąć plik i pracować z zawartością w pamięci, nie troszcząc się już o dysk I/O.

Teraz najlepszym sposobem, aby przeprowadzić kompletne iteracji nad plikiem według standardowej biblioteki jest coś wzdłuż linii

std::ifstream ifs(filename, std::ios::binary); 
    for (std::istreambuf_iterator<char, std::char_traits<char> > it(ifs.rdbuf()); 
     it != std::istreambuf_iterator<char, std::char_traits<char> >(); it++) { 
    // do something with *it; 
    } 
ifs.close(); 

lub użyć std::copy, ale nawet z std::copy zawsze jesteś przy użyciu istreambuf iteratory (więc jeśli dobrze rozumiem dokumentację C++, zasadniczo czytasz 1 bajt przy każdym wywołaniu z poprzednim kodem).

Pytanie brzmi: jak utworzyć niestandardowy iterator? skąd powinienem dziedziczyć?

Zakładam, że jest to również ważne podczas zapisywania pliku na dysk i zakładam, że mógłbym użyć tej samej klasy iteratora do napisania, jeśli się mylę, proszę, popraw mnie.

+0

Czy * rozmiar * danych przychodzących wyklucza Cię tylko ['ifs.read'] (http://en.cppreference.com/w/cpp/io/basic_istream/read) - dane prosto do góry w 'std :: vector ' i iterowanie nad tym? – WhozCraig

+0

@WhozCraig na razie Nie sądzę, że plik jest zbyt duży, aby przechowywać go w pamięci (jeśli to jest to, co masz na myśli), mam się dobrze z 'odczytaniem 'lub w jakikolwiek inny sposób, nawet konstruktorem 'klasa' vector' obsługuje iteratory, więc jestem w porządku po tej stronie, "problemem" są same iteratory, chciałbym napisać jeden, aby spróbować inaczej przeglądać dane. EDIT: Chciałbym uniknąć jakiejkolwiek C-ish sposób, będę trzymać się z iteratorów. – user2485710

+1

* zasadniczo czytasz 1 bajt przy każdym wywołaniu * - z bufora '' ifstream' w pamięci, a nie z samego pliku. Rzeczywiste wywołania read (2) są nadal dla każdego 4k lub 16k lub cokolwiek jest domyślnym buforem dla ciebie. – Cubbi

Odpowiedz

1

Możliwe jest zoptymalizowanie przy użyciu std::istreambuf_iterator<char>, ale prawie żadna implementacja nie. Samo wywodzenie się z czegoś tak naprawdę nie wystarczy, ponieważ nie jest tak, jak działają iteratory.

Najskuteczniejszym wbudowaną podejście jest chyba po prostu zrzucić plik do std::ostringstream i dostać std::string stamtąd:

std::ostringstream out; 
out << file.rdbuf(); 
std::string content = out.str(); 

Jeśli chcesz uniknąć podróży przez std::string można napisać strumień bufor bezpośrednio przesyłający zawartość do obszaru pamięci lub std::vector<unsigned char>, a także wykorzystując powyższą operację wyjściową.

Zasadniczo mogą one mieć backdoora do bufora strumieniowego i omijać operacje według znaków. Bez tego backdoor nie będzie w stanie przyspieszyć niczego za pomocą tych iteratorów. Możesz może utworzyć iterator na buforach strumieniowych, używając bufora strumieniowego o numerze sgetn() do obsługi podobnego bufora. W takim przypadku będziesz potrzebował wersji std::copy() zajmującej się segmentami (np. Każdym wypełnieniem bufora). Oprócz tego po prostu odczytywałbym plik do bufora używając bufora strumieniowego i iterowałam go.

+0

, więc sugerujesz, że zasadniczo trzymasz się mojej pierwszej implementacji? Jakie są możliwe błędy? Co stanie się, jeśli plik zostanie uszkodzony? – user2485710

1

Moja sugestia nie dotyczy używania strumienia niestandardowego, bufora strumieniowego lub stream-iteratora.

#include <fstream> 

struct Data { 
    short a; 
    short b; 
    int c; 
}; 

std::istream& operator >> (std::istream& stream, Data& data) { 
    static_assert(sizeof(Data) == 2*sizeof(short) + sizeof(int), "Invalid Alignment"); 
    if(stream.read(reinterpret_cast<char*>(&data), sizeof(Data))) { 
     // Consider endian 
    } 
    else { 
     // Error 
    } 
    return stream; 
} 

int main(int argc, char* argv[]) 
{ 
    std::ifstream stream; 
    Data data; 
    while(stream >> data) { 
     // Process 
    } 
    if(stream.fail()) { 
     // Error (EOF is good) 
    } 
    return 0; 
} 

Można odważył się dokonać elementy bufor strumienia iterator czytanie mający większy rozmiar niż pod nią char_type:

  • Co jeśli dane ma nieprawidłowy format?
  • Co jeśli dane są niekompletne i EOF?

Stan strumienia nie jest utrzymywany przez bufor lub iterator.

+0

Czy mogę buforować cały plik? – user2485710

+0

@ user2485710 To będzie zależeć od bufora strumienia bazowego (dlatego jest to możliwe) –

Powiązane problemy