2012-01-26 19 views
9

Necesito una secuencia de archivo de entrada que tendría un iterador/adaptador bidireccional.iterador bidireccional sobre archivo/ifstream

Desafortunadamente std::ifstream (y similar) se puede usar solo con std::istream_iterator que es un tipo de iterador directo que no puede retroceder. (¿O me equivoco aquí?)

Pude simplemente cargar todo el archivo en la memoria y luego usar un iterador de acceso aleatorio mucho más potente sobre la matriz; Sin embargo, me gustaría evitar eso y leer solo todo lo que realmente necesito. Puede suceder que realmente necesite solo una pequeña porción de un archivo.

que de alguna manera podría hacerlo de forma manual utilizando C stdio.h funciones, pero que va a ser doloroso. Básicamente, necesitaría implementar un iterador bidireccional, con todas sus especificaciones en mente, a mano.

Estoy considerando buscar en la biblioteca de boost iostream, pero el manual es algo abrumador, ¿esperaba que alguien pudiera darme la mano para lograr este objetivo en particular? ¿O tal vez hay otra biblioteca ya existente para hacer exactamente lo que necesito?

necesito el iterador para la biblioteca impulso Xpressive para analizar mis archivos, que espera que el iterador puede ser incrementado, así como disminuye. Estaría bien si el archivo que estoy leyendo está almacenado en el búfer, aunque esto no es un requisito.

¿Alguna idea? ¡Gracias!

+1

¿Estás seguro de que necesitas un iterador bidireccional? Si un iterador directo será suficiente, [Boost.Spirit] (http://www.boost.org/libs/spirit/) lo tiene cubierto: [Bibliotecas de apoyo -> El iterador de múltiples pasos] (http: //www.boost .org/libs/spirit/doc/html/spirit/support/multi_pass.html). – ildjarn

+0

¿No puede almacenar una parte del archivo en el búfer, realizar operaciones en él, escribirlo en un archivo temporal, luego obtener la siguiente parte del archivo, etc.? –

+1

Supongo que no se puede simplemente asignar un mapa de memoria al archivo? Menos portátil, por supuesto, pero te da acceso aleatorio * y * solo lee las partes del archivo que realmente necesitas (bueno, los vecindarios de esas partes se redondean a algún tamaño de página). –

Respuesta

5

Si tuviera que atravesar un archivo en la dirección incorrecta, comenzaría a cuestionar mis requisitos. Esto parece ser una forma artificial de hacer las cosas y lo más probable es que algo se haya arruinado dramáticamente en alguna parte.

Una vez que he confirmado que este es realmente el requisito, me doy cuenta de que definitivamente estamos hablando de archivos aquí, en lugar de, por ejemplo, una tubería con nombre o un zócalo. Es decir, sería posible el mapa de memoria al menos partes del archivo. Yo usaría esto para crear un iterador que recorre la memoria. Dado que obviamente se necesita un iterador, no hay necesidad de involucrar transmisiones. Si también se necesitaran flujos, yo aún haría un mapa de la memoria del archivo y los búferes invertidos desde atrás en un búfer de transmisión personalizado.

Cuando realmente lee desde el principio y solo necesita poder retroceder cuando es necesario, puede ser más simple que esto: mantener un búfer de los datos ya leídos y gastarlo cuando se mueve al final y posiblemente leer todo el archivo si el iterador final se utiliza para retroceder debería abordar esto. Aquí es el código que sin duda puede leer un archivo hacia delante y hacia atrás, pero no se ha probado a fondo:

#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 con crear un bidirectional_stream con la corriente que desea leer como un argumento y luego usar los métodos y begin()end() a realidad acceder a él.

+0

"* Si tuviera que atravesar un archivo en la dirección equivocada, Comenzaría cuestionando mis requisitos. * "Estos son los requisitos de [Boost.Xpressive] (http://www.boost.org/libs/xpressive/), no los requisitos directos del OP. – ildjarn

+0

Luego el autor original es ¡Usar Boost.Xpressive en las cosas incorrectas! Leer un archivo al revés no va a ser demasiado eficiente. Si va a ser utilizado para alguna biblioteca es completamente más allá del punto: el requisito de leer la parte posterior del archivo orden equivocado parece estar allí y me pregunta que de ser necesario. –

+3

lo sentimos, pero ejecuta una expresión regular sobre el contenido del archivo no suena como una cosa descabellada que hacer. el uso de una secuencia de archivo como entrada para la expresión regular puede ser equivocada, pero el objetivo general sin duda no es es cuestionable – ildjarn

3

Puesto que ya está utilizando impulso, echar un vistazo a boost::iostreams::mapped_file_sourcehttp://www.boost.org/doc/libs/release/libs/iostreams/doc/classes/mapped_file.html#mapped_file_source

Puede utilizar file.data() como el iterador y comenzar file.data() + file.size() como el iterador final .

+0

Definitivamente investigaré el mapped_file_source. Sería realmente útil si pudieras darme un ejemplo de cómo usarlo para lograr lo que necesito. – CygnusX1

+0

@ CygnusX1 Simplemente escriba 'boost :: iostreams :: mapped_file_source file (" whatever.bin ");' y tendrá su archivo, que puede iterar al revés, p. 'Reverse_copy (file.data(), file.data() + file.size(), std :: ostreambuf_iterator (std :: cout));' o de hecho, el acceso al azar. – Cubbi

+0

Er, tengo que make_iterator_range antes de que puedas reverse_copy desde un par de punteros, no era el mejor ejemplo. – Cubbi