2012-05-30 12 views
11

¿Hay alguna manera de leer un archivo al revés, línea por línea, sin tener que pasar por el archivo desde el principio para comenzar a leer hacia atrás?¿Lee un archivo al revés?

+2

Puedes leer las líneas del archivo en un 'vector' y retroceder sobre él. – hmjd

+0

@hmjd Esto debería ser una respuesta, IMO. – jrok

Respuesta

8

Como por comentario, una alternativa posible (bastante simple) sería leer las líneas en un vector.Por ejemplo:

#include <iostream> 
#include <fstream> 
#include <string> 
#include <vector> 

int main() 
{ 
    std::ifstream in("main.cpp"); 

    if (in.is_open()) 
    { 
     std::vector<std::string> lines_in_reverse; 
     std::string line; 
     while (std::getline(in, line)) 
     { 
      // Store the lines in reverse order. 
      lines_in_reverse.insert(lines_in_reverse.begin(), line); 
     } 
    } 
} 

EDIT:

Según jrok 's y Loki Astari los comentarios, push_back() sería más eficiente, pero las líneas estarían en orden de archivo, la iteración de manera inversa (reverse_iterator) o std::reverse() haría sea ​​necesario:

std::vector<std::string> lines_in_order; 
    std::string line; 
    while (std::getline(in, line)) 
    { 
     lines_in_order.push_back(line); 
    } 
+3

Puede usar 'std :: reverse' después de completar la lectura o simplemente iterar con' reverse_iterator'. – jrok

+0

@jrok, el código se inserta en la parte delantera del vector tal como está, por lo que las líneas ya estarán en orden inverso, por lo que no se requiere ninguna forma de inversión. Si se utilizó 'push_back', entonces sí, use una forma de inversión . – hmjd

+1

¿Voy a adivinar que mover la búsqueda a la parte posterior del archivo y luego iterar hacia delante sería mejor para el rendimiento? – mezamorphic

3

La respuesta corta sería no. Sin embargo, puede usar la función seek() para mover su puntero a donde desee ir. Luego lea() algunos datos de ese punto. Si sabe cómo administrar los búferes, debería ser bastante rápido porque puede leer y almacenar en caché los datos y luego buscar los caracteres de nueva línea anteriores. Divertirse con \ r \ n que se invierte ...

- Actualización: alguna elaboración en el algoritmo posible -

Esto no es un código válido, pero debe darle una idea de lo que yo estoy tratando de decir aquí

lecturas de archivos:

int fpos = in.size() - BUFSIZ; 
char buf[BUFSIZ]; 
in.seek(fpos); 
in.read(buf, BUFSIZ); 
fpos -= BUFSIZ; // repeat until fpos < 0, although think of size % BUFSIZ != 0 
// now buf has characters... reset buffer position 
int bpos = BUFSIZ - 1; 

Conseguir cadena:

// first time you need to call the read 
if(bpos == -1) do_a_read(); 
// getting string 
std::string s; 
while(bpos >= 0 && buf[bpos] != '\n') { 
    s.insert(0, 1, buf[bpos]); 
    --bpos; 
} 
// if bpos == -1 and buf[0] != '\n' then you need to read another BUFSIZ chars 
// and repeat the previous loop... 

// before leaving, skip all '\n' 
while(bpos >= 0 && buf[bpos] == '\n') { 
    --bpos; 
} 
return s; 

Para facilitar con '\ r', podría tener un primer pase que transforma todo '\ r' en '\ n'. De lo contrario, todas las pruebas de '\ n' también deben probar para '\ r'.

+0

¿Podría explicar el punto de los buffers por favor? – mezamorphic

+0

@ user1107474: "línea por línea" es solo una cuestión de interpretar bytes '\ r \ n' como líneas nuevas. Si lee los bytes al revés e interpreta los bytes de la nueva línea usted mismo, puede leer las líneas hacia atrás. Simplemente no es una funcionalidad estándar. – MSalters

+0

Una pequeña nota aquí ... la entrada (0, 1, char) es LENTA. Si desea acelerar, querrá guardar el punto final (bpos antes de ingresar el tiempo) y el punto de inicio (bpos después del tiempo) y agregar esa cadena al resultado de una vez. –

5
  1. Abrir el archivo para lectura, llamada fseek() a buscar al final del archivo, a continuación, llamar ftell() para obtener la longitud del archivo. Alternativamente, puede obtener la longitud del archivo llamando al stat() o fstat()

  2. Asigne un puntero del búfer al tamaño del archivo obtenido en el n. ° 1 anterior.

  3. Lea todo el archivo en ese búfer - probablemente pueda usar fread() para leer el archivo todo de una vez (suponiendo que el archivo es lo suficientemente pequeño).

  4. Utilice otro puntero de char para recorrer el archivo de principio a fin de la memoria intermedia.

+1

Suponiendo que los archivos pueden caber fácilmente en la memoria, esta es una buena y fácil solución. –

11

Usa un memory-mapped file y camina hacia atrás. El sistema operativo buscará en las partes necesarias del archivo en orden inverso.

+0

También es una idea, aunque a menudo mapear un archivo no es exactamente sencillo (¡especialmente la primera vez!) –

2

versión ligeramente mejorada será la siguiente: -
1) tratar de la última-1 posición
2) Obtenga la posición de los últimos 1
3) Lea un char e imprímalo;
4) seek 2 pos back;
5) repite 3 & 4 para last-1 veces;

ifstream in; 
    in.open("file.txt"); 
    char ch; 
    int pos; 
    in.seekg(-1,ios::end); 
    pos=in.tellg(); 
    for(int i=0;i<pos;i++) 
    { 
     ch=in.get(); 
     cout<<ch; 
     in.seekg(-2,ios::cur); 
    } 
    in.close(); 
Cuestiones relacionadas