2013-11-21 13 views
5

This answer sottolinea il fatto che C++ non è adatto per l'iterazione su un file binario, ma questo è ciò di cui ho bisogno in questo momento, in breve ho bisogno di operare su file in modo "binario", sì tutti i file sono binari anche i file .txt, ma sto scrivendo qualcosa che opera su file immagine, quindi ho bisogno di leggere file che sono ben strutturati, se i dati sono disposti in modo specifico.File binari ed iteratori C++: con 1: 1 utilizzando ifstreambuf_iterator?

Mi piacerebbe leggere l'intero file in una struttura dati come std::vector<T> in modo da poter chiudere quasi immediatamente il file e lavorare con il contenuto in memoria senza preoccuparmi più dell'I/O del disco.

In questo momento, il modo migliore per eseguire un'iterazione completo su un file in base alla libreria standard è qualcosa sulla falsariga di

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

o utilizzare std::copy, ma anche con std::copy utilizzare sempre istreambuf iteratori (quindi se capisco correttamente la documentazione del C++, stai praticamente leggendo 1 byte per ogni chiamata con il codice precedente).

Quindi la domanda è: come posso scrivere un iteratore personalizzato? da dove dovrei ereditare?

Suppongo che questo sia importante anche durante la scrittura di un file su disco, e presumo che potrei usare la stessa classe di iteratore per scrivere, se ho torto non esitate a correggermi.

+0

La * dimensione * dei dati in entrata ti preclude solo da ['ifs.read'] (http://en.cppreference.com/w/cpp/io/basic_istream/read) - consente di allineare i dati in un 'std :: vector ' e iterando su quello? – WhozCraig

+0

@WhozCraig per ora non penso che il file sia troppo grande per essere tenuto in memoria (se questo è ciò a cui ti riferisci), sto bene con 'read' o in qualsiasi altro modo, anche il costruttore del La classe 'vector' supporta gli iteratori, quindi sto bene da quella parte, il" problema "sono gli iteratori stessi, vorrei scriverne uno per cercare di sfogliare i dati in modo diverso. EDIT: Vorrei evitare ogni modo C-ish, rimarrò con gli iteratori. – user2485710

+1

* stai praticamente leggendo 1 byte ad ogni chiamata * - dal buffer in memoria di 'ifstream', non dal file stesso. Le chiamate attuali di lettura (2) sono ancora per ogni 4k o 16k o qualunque sia il buffer predefinito per te. – Cubbi

risposta

1

È possibile ottimizzare std::copy() utilizzando std::istreambuf_iterator<char> ma quasi nessuna implementazione. Solo il fatto di derivare da qualcosa non lo farà davvero, perché non è così che funzionano gli iteratori.

Il più efficace approccio integrato è probabilmente per scaricare semplicemente il file in un std::ostringstream e ottenere un std::string da lì:

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

Se si vuole evitare di viaggiare attraverso un std::string si potrebbe scrivere un ruscello buffer direttamente scaricare il contenuto in un'area di memoria o un std::vector<unsigned char> e anche utilizzando l'operazione di output di cui sopra.

Gli std::istreambuf_iterator<char> s potrebbero, in linea di principio, avere una backdoor per il buffer del flusso e bypassare le operazioni a caratteri. Senza quella backdoor non sarai in grado di accelerare nulla usando questi iteratori. È possibile creare un iteratore sopra i buffer di flusso utilizzando il buffer dello stream sgetn() per gestire un buffer simile. In tal caso avresti praticamente bisogno di una versione di std::copy() che si occupi di segmenti (ad esempio, ogni riempimento di un buffer) in modo efficiente. A meno che non mi piacerebbe solo leggere il file nel buffer utilizzando un buffer di flusso e scorrere su quello.

+0

quindi stai suggerendo fondamentalmente di restare con la mia prima implementazione? Quali sono gli errori possibili? Cosa succede se il file è corrotto? – user2485710

1

Il mio suggerimento non è quello di utilizzare un flusso personalizzato, stream-buffer o stream-iterator.

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

Si potrebbe osare fare un elementi di lettura iteratore buffer di flusso aventi una dimensione più grande rispetto alla char_type underlaying:

  • Che cosa succede se i dati ha un formato non valido?
  • Cosa succede se i dati sono incompleti e in EOF?

Lo stato del flusso non viene gestito dal buffer o dall'iteratore.

+0

Posso tamponare l'intero file? – user2485710

+0

@ user2485710 Ciò dipenderebbe dal buffer del flusso sottostante (quindi è possibile) –

Problemi correlati