El uso de mapeo de memoria para archivos grandes en Delphi
Rara vez uso el mapeo de memoria de archivos grandes. Me encontré con tal requisito, así que registré el proceso para brindarles una introducción. Debido a que la aplicación no es complicada, puede haber cosas que no se puedan considerar.
Para algunos archivos pequeños, se pueden utilizar secuencias de archivos normales para resolver el problema. Sin embargo, para archivos muy grandes, como 2G o más, las secuencias de archivos no funcionarán, por lo que es necesario utilizar los métodos relacionados con el mapeo de memoria de la API, incluso. si se trata de mapeo de memoria, tampoco se puede mapear todo el tamaño del archivo a la vez, por lo que el mapeo debe realizarse en fragmentos y procesar una pequeña porción a la vez.
Veamos primero algunas funciones.
CreateFile: abre un archivo
GetFileSize: Obtener el tamaño del archivo
CreateFileMapping: Crear mapeo
MapViewOfFile: archivo de mapeo
Si observa la ayuda de MapViewOfFile, sus dos últimos parámetros deben ser un múltiplo entero de la granularidad de la página. Generalmente, la granularidad de la página de la máquina es 64k (65536 bytes). Cualquier posición, cualquier longitud es posible, por lo que es necesario realizar algún procesamiento.
La tarea de este ejemplo es leer los valores de longitud secuencialmente de una lista de longitud (FInfoList) y luego leer secuencialmente los datos de la longitud especificada de otro archivo grande (FSourceFileName). Si es un archivo pequeño, esto es más fácil. para manejar Simplemente lea la secuencia del archivo una vez y luego léala en secuencia. Para archivos grandes, necesitamos cambiar constantemente la posición del mapeo para obtener los datos que queremos.
Este ejemplo muestra que la granularidad de la página se obtiene primero a través de GetSystemInfo, y luego la granularidad de la página se usa 10 veces como un bloque de datos mapeado en el bucle for, se juzgará si la longitud de lectura (total) más la longitud a leer. están dentro de este tiempo Dentro del rango de mapeo (10 veces la granularidad de la página), si es así, continúe leyendo. Si se excede, se deben registrar los datos restantes y luego se debe reasignar el siguiente bloque de memoria y los datos restantes registrados se deben fusionar. en la nueva lectura Los datos obtenidos son un poco complicados (tal vez mi idea sea demasiado complicada),
El código se enumera a continuación.
procedimiento TGetDataThread.DoGetData; var FFile_Handle:THandle; FFile_Map:THandle; lista:TStringList; p:PChar;intervalo:Integer;= 0; TMemoryStream.Create; lista := TStringList.Create; //Obtener información del sistema GetSystemInfo(sysinfo); //Granularidad de asignación de página blockize := sysinfo.dwAllocationGranularity; //Abrir archivo FFile_Handle := CreateFile(PChar(FSourceFileName),GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0); FFile_Handle = INVALID_HANDLE_VALUE luego Salir; // Obtener el tamaño del archivo := GetFileSize(FFile_Handle,nil); //Crear mapeo FFile_Map := CreateFileMapping(FFile_Handle,nil,PAGE_READONLY,0,0,nil); //Aquí hemos asignado 10 veces el tamaño del bloque en un bloque de datos. Si el tamaño del archivo es menor que 10 veces el tamaño del bloque, toda la longitud del archivo se asignará directamente si tamaño del bloque div > 10, entonces readlen := 10*blocksize si no readlen :=. tamaño de archivo para i := 0 a FInfoList.Count - 1 comenzar list.Delimiter := ':' list.DelimitedText := FInfoList.Strings[i]; //Obtener la longitud, hice el análisis aquí, porque la información que almacené es de tipo a:b:c, por lo que len está separado por ::= StrToInt(list.Strings[1] intervalo := StrToInt(list); .Strings[2]); si (i = 0) o (totallen+len >=readlen) entonces comienza //Si la longitud de lectura más la longitud a leer es mayor que 10 veces el tamaño del bloque, entonces necesitamos retener el contenido al final del mapeo anterior para fusionarlo con el nuevo contenido del mapeo si i > 0 entonces comenzar offset := offset + readlen ; //Escribe en la secuencia temporal tstream.Write(p^,readlen-totallen); tstream.Position := 0; granularidad, luego asigne directamente la longitud restante si el desplazamiento del tamaño de archivo <tamaño de bloque entonces readlen := tamaño de archivo-offset; //Mapeo, p es un puntero al área de mapeo //Tenga en cuenta que el tercer parámetro aquí siempre se establece en 0. Este valor debe establecerse de acuerdo con la situación real p:= PChar(MapViewOfFile( FFile_Map,FILE_MAP_READ, 0,offset,readlen)); //Si hay datos en la secuencia temporal, es necesario fusionarlos si tstream.Size > 0 luego comenzar. // Copia los datos de la secuencia temporal sobre stream.CopyFrom(tstream,tstream.Size); // Luego escribe nuevos datos al final y fusiona para completar stream.Write(p^,len-tstream.Size); - tstream.Size; //Mueve la posición del puntero para que apunte al comienzo de los siguientes datos Inc(p,len-tstream.Size); tstream.Clear; total:= total + len; Inc(p,len); end; stream.Position:= 0; //Guardar la secuencia en un archivo stream.SaveToFile(IntToStr(i)+'.txt'); Gratis; tstream.Free; CloseHandle(FFile_Handle); CloseHandle(FFile_Map final);
Si tiene alguna pregunta, deje un mensaje o vaya a la comunidad de este sitio para comunicarse y discutir. Gracias por leer. Espero que pueda ayudar a todos. ¡Gracias por su apoyo a este sitio!