2011-01-17 13 views
6

Estoy trabajando con archivos de texto muy grandes, 2 GB y más. Me gustaría tener una función similar a Seek(). ¿Alguien ha hecho algo así? Cargando a TStringList está fuera de la cuestión. También trabaja con archivos sin tipo también. Por ahora estoy usando readLn, pero eso dura demasiado. Gracias.Buscar archivo de texto

+1

No está claro para mí lo que se supone que la función de búsqueda va a hacer, busque un desplazamiento particular en bytes? buscar un número de línea? ¿o que? – jachguate

+0

Al cargar 2,5 GB en la memoria, lo cargo por páginas, 300000 líneas a la vez. Creo el índice y paso a la página siguiente. Pero eso me da solo acceso secuencial, y me gustaría un acceso directo. – Mihaela

+0

para archivos tan grandes, ya que los archivos de memoria asignada no siempre funcionarán, por lo que su carga por páginas es el mejor enfoque. –

Respuesta

13

Asigne el archivo a la memoria (CreateFileMapping/MapViewOfFile) por piezas, luego escanee la memoria mapeada y cree un índice, la lista de posiciones de cada inicio de línea. Luego, su operación de búsqueda se realizará al obtener la posición de la línea N en el archivo y buscar esta posición. Use TFileStream para realizar un acceso aleatorio al archivo o, si solo lee el archivo, también puede usar asignaciones de archivos para acceso aleatorio; esto podría ser aún más rápido que usar TFileStream en paralelo a la asignación de archivos.

+1

+1 asignar el archivo a la memoria –

+0

¿Cómo va a ayudar el mapeo del archivo a la memoria con el escaneo inicial? Dudo que sea más rápido que la lectura secuencial de bloque a bloque (es decir: configure un búfer que sea un múltiplo del tamaño de bloque de su sistema de archivos y léalo) –

+0

@Cosmin Un poco menos de operaciones: con MapViewOfFile obtiene un puntero que usted puede pasar de inmediato a alguna parte Con ReadFile tiene un búfer, que pasa a ReadFile API, que llama a NtReadFile, que llama a ZwReadFile, que emite un IRP que recorre toda la pila de controladores. La diferencia es mínima, sin embargo, existe. –

2

Ha establecido algunas condiciones de borde bastante difíciles.

Lo único que puedo imaginar es intentar obtener el control del archivo de texto y usar las funciones de win32 para buscar directamente. Sin embargo, ten cuidado con el almacenamiento en caché de archivos de texto.

Si las bases de código grandes que usan writeln/readln son la razón, implementar su propio controlador de archivo de texto que lo permita (o simplifique el almacenamiento en caché) podría ser la solución.

Free Pascal tiene una función getfilehandle para este propósito, para recuperar el controlador del sistema operativo desde los archivos de archivo de texto/tfilerec. No sé qué agregar Delphi reciente en este departamento.

2

Si necesita granularidad de nivel de línea en lugar de nivel de byte, no hay forma de evitar leer todo el archivo al menos una vez para encontrar los marcadores de final de línea (LF o CRLF, dependiendo de su entorno .) Este es un límite difícil: no se puede saber de antemano cuál será el final de la línea.

Después de construir el índice de desplazamiento de fin de línea a byte, es posible que lo almacene en el disco y utilice una heurística a la "última hora de modificación" para comprobar si el índice necesita regenerarse (necesita una heurística porque puede no se asegure de que el contenido del archivo no haya cambiado excepto al leerlo, y entonces también podría reconstruir el índice ya que de todos modos estará vinculado a E/S)

Según lo sugerido por otros, el mecanismo subyacente tendrá que ser CreateFileMapping/CreateViewOfFile (o mmap en POSIX)

3

Pruebe GpHugeFile.

Encapsulación de rutinas de manejo de archivos de Windows que permite trabajar con archivos de> 2GB.

Se incluye soporte para acceso sin búfer (FILE_FLAG_NO_BUFFERING) y almacenamiento en búfer para archivos accedidos secuencialmente. Incluye también la clase de contenedor de flujo.

1

Se puede usar esta función para cambiar la posición actual en un archivo TextoSI:

function TextSeek(var f: Text; position: Int64): boolean; 
var pos64: Int64Rec absolute position; 
    resHi: cardinal; 
begin 
    result := false; 
    with TTextRec(f) do 
    begin 
    if mode<>fmInput then 
     exit; 
    resHi := pos64.Hi; 
    if (SetFilePointer(handle,pos64.Lo,@resHi,FILE_BEGIN)<>pos64.Lo) or 
     (resHi<>pos64.Hi) then 
     exit; 
    BufEnd := 0; // flush internal reading buffer 
    BufPos := 0; 
    result := true; // success 
    end; 
end; 

Se devolverá true en caso de éxito, falso en caso de error (no válido posición del archivo no abierto).

Si desea tener un acceso rápido, asegúrese de haber establecido {$ I-} y comprobar IOResult a mano, y haber llamado a System.SetTextBuffer() con algún búfer (1 KB hasta 64 KB podría tener sentido) .