2012-01-26 18 views
9

Ho bisogno di un flusso di file di input che abbia un iteratore/adattatore bidirezionale.iteratore bidirezionale su file/ifstream

Sfortunatamente std::ifstream (e simili) può essere utilizzato solo con std::istream_iterator che è un tipo di iteratore di inoltro che non può tornare indietro. (o mi sbaglio qui?)

Potrei semplicemente caricare l'intero file in memoria e quindi utilizzare un iteratore di accesso casuale molto più potente sull'array; comunque vorrei evitarlo e leggere solo quanto ho veramente bisogno. Può succedere che abbia davvero bisogno solo di una piccola parte di un file.

In qualche modo potrei farlo manualmente utilizzando le funzioni C stdio.h, ma sarà doloroso. Fondamentalmente ho bisogno di implementare un iteratore bidirezionale, con tutte le sue specifiche in mente, a mano.

Sto pensando di esaminare la libreria iostream di boost, ma il manuale è piuttosto travolgente, speravo che qualcuno potesse darmi una mano per raggiungere questo obiettivo particolare? O forse c'è un'altra libreria già esistente per fare esattamente quello di cui ho bisogno?

Ho bisogno dell'iteratore della libreria boost xpressive per analizzare i miei file, che si aspetta che l'iteratore possa essere incrementato o decrementato. Sarei a posto se il file che sto leggendo è bufferizzato, sebbene questo non sia un requisito.

Qualche idea? Grazie!

+1

Sei sicuro di aver bisogno di un iteratore bidirezionale? Se un iteratore forward è sufficiente, [Boost.Spirit] (http://www.boost.org/libs/spirit/) hai coperto: [Librerie di supporto -> Multipass iterator] (http: //www.boost .org/libs/spirito/doc/html/spirito/supporto/multi_pass.html). – ildjarn

+0

Non si può bufferizzare una parte del file, eseguire le operazioni su di esso, scriverlo in un file temporaneo, quindi ottenere la parte successiva del file, ecc ... ?? –

+1

Lo prendo non si può semplicemente mappare la memoria del file? Meno portabile, ovviamente, ma ti dà accesso casuale * e * legge solo le parti del file che ti servono (beh, i quartieri di quelle parti arrotondati ad alcune dimensioni della pagina). –

risposta

5

Se dovessi attraversare un file nella direzione sbagliata, inizierei a mettere in discussione le mie esigenze. Questo sembra essere un modo forzato di fare le cose e molto probabilmente qualcosa è stato incasinato in modo drammatico da qualche parte.

Una volta che ho confermato che questo è davvero il requisito, mi renderei conto che stiamo sicuramente parlando di file qui, piuttosto che ad es. una pipa con nome o una presa. Cioè, sarebbe possibile mappare la memoria almeno in parti del file. Lo userei per creare un iteratore che cammina nella memoria. Poiché ovviamente è necessario un iteratore, non è necessario coinvolgere i flussi. Se fossero necessari anche i flussi, avrei comunque memorizzato la mappatura del file e invertire i buffer dal retro in un buffer di flusso personalizzato.

Quando in realtà si sta leggendo dall'inizio e si ha solo bisogno di essere in grado di tornare indietro quando necessario, potrebbe essere più semplice di questo: mantenere un buffer dei dati già letti e spenderli quando si sposta dalla fine più eventualmente leggendo l'intero file se l'iteratore finale è utilizzato per spostarsi all'indietro dovrebbe indirizzarlo. Qui è il codice che certamente può leggere un file in avanti e indietro, ma non è testato:

#include <iostream> 
#include <fstream> 
#include <algorithm> 
#include <iterator> 
#include <limits> 
#include <vector> 

class bidirectional_stream 
{ 
public: 
    class           iterator; 
    typedef iterator        const_iterator; 
    typedef std::reverse_iterator<iterator>  reverse_iterator; 
    typedef std::reverse_iterator<const_iterator> const_reverse_iterator; 

    bidirectional_stream(std::istream& in): 
     in_(in) 
    { 
    } 
    iterator   begin(); 
    iterator   end(); 
    reverse_iterator rbegin(); 
    reverse_iterator rend(); 

    bool expand() 
    { 
     char buffer[1024]; 
     this->in_.read(buffer, sizeof(buffer)); 
     this->buffer_.insert(this->buffer_.end(), buffer, buffer + this->in_.gcount()); 
     return 0 < this->in_.gcount(); 
    } 
    long read_all() 
    { 
     this->buffer_.insert(this->buffer_.end(), 
          std::istreambuf_iterator<char>(this->in_), 
          std::istreambuf_iterator<char>()); 
     return this->buffer_.size(); 
    } 
    char get(long index) { return this->buffer_[index]; } 
    long current_size() const { return this->buffer_.size(); } 
private: 
    std::istream&  in_; 
    std::vector<char> buffer_; 
}; 

class bidirectional_stream::iterator 
{ 
public: 
    typedef char       value_type; 
    typedef char const*      pointer; 
    typedef char const&      reference; 
    typedef long       difference_type; 
    typedef std::bidirectional_iterator_tag iterator_category; 

    iterator(bidirectional_stream* context, size_t pos): 
     context_(context), 
     pos_(pos) 
    { 
    } 

    bool operator== (iterator const& other) const 
    { 
     return this->pos_ == other.pos_ 
      || (this->pos_ == this->context_->current_size() 
       && !this->context_->expand() 
       && other.pos_ == std::numeric_limits<long>::max()); 
    } 
    bool operator!= (iterator const& other) const { return !(*this == other); } 
    char  operator*() const { return this->context_->get(this->pos_); } 
    iterator& operator++() { ++this->pos_; return *this; } 
    iterator operator++(int) { iterator rc(*this); this->operator++(); return rc; } 
    iterator& operator--() 
    { 
     if (this->pos_ == std::numeric_limits<long>::max()) 
     { 
      this->pos_ = this->context_->read_all(); 
     } 
     --this->pos_; 
     return *this; 
    } 
    iterator operator--(int) { iterator rc(*this); this->operator--(); return rc; } 

private: 
    bidirectional_stream* context_; 
    long     pos_; 
}; 

bidirectional_stream::iterator bidirectional_stream::begin() 
{ 
    return iterator(this, 0); 
} 
bidirectional_stream::iterator bidirectional_stream::end() 
{ 
    return iterator(this, std::numeric_limits<long>::max()); 
} 

bidirectional_stream::reverse_iterator bidirectional_stream::rbegin() 
{ 
    return reverse_iterator(this->end()); 
} 
bidirectional_stream::reverse_iterator bidirectional_stream::rend() 
{ 
    return reverse_iterator(this->begin()); 
} 

basta creare un bidirectional_stream con il flusso che si desidera leggere come un argomento e quindi utilizzare le begin() e end() metodi per davvero accedervi

+0

"* Se dovessi attraversare un file nella direzione sbagliata, Vorrei iniziare a mettere in discussione le mie richieste. * "Questi sono i requisiti di [Boost.Xpressive] (http://www.boost.org/libs/xpressive/), non i requisiti diretti dell'OP – ildjarn

+0

Quindi l'autore originale è usare Boost.Xpressive sulle cose sbagliate! Leggere un file nel modo sbagliato non sarà troppo efficiente. Il fatto che debba essere usato per qualche libreria è del tutto diverso dal punto: la necessità di leggere il retro del file Sembra che ci sia un ordine errato e vorrei chiedergli se fosse necessario –

+3

Scusa, ma eseguire un'espressione regolare sul contenuto del file non sembra una cosa bizzarra da fare. Usare un flusso di file come input per la regex può essere fuorviante, ma il l'obiettivo generale è certamente no t discutibile. – ildjarn

3

Dal momento che si sta già utilizzando spinta, uno sguardo a boost::iostreams::mapped_file_sourcehttp://www.boost.org/doc/libs/release/libs/iostreams/doc/classes/mapped_file.html#mapped_file_source

È possibile utilizzare file.data() come l'inizio iteratore e file.data() + File.Size() come iteratore fine .

+0

Cercherò sicuramente di mapped_file_source. Sarebbe molto utile, se potessi fornirmi un esempio su come usarlo per ottenere ciò di cui ho bisogno? – CygnusX1

+0

@ CygnusX1 Basta scrivere 'boost :: iostreams :: mapped_file_source file (" whatever.bin ");' e hai il tuo file, che puoi ripetere al contrario, ad es. 'reverse_copy (file.data(), file.data() + file.size(), std :: ostreambuf_iterator (std :: cout));' o infatti, l'accesso casuale. – Cubbi

+0

Er, devi make_iterator_range prima di poter eseguire reverse_copy da una coppia di puntatori, non era l'esempio migliore. – Cubbi