L'utilisation du mappage de mémoire pour les fichiers volumineux dans Delphi
J'utilise rarement le mappage mémoire de fichiers volumineux. Il m'est arrivé de rencontrer une telle exigence, j'ai donc enregistré le processus pour vous donner une introduction. Parce que l'application n'est pas compliquée, il peut y avoir des choses qui ne peuvent pas être prises en compte.
Pour certains petits fichiers, des flux de fichiers ordinaires peuvent être utilisés pour résoudre le problème. Cependant, pour les fichiers très volumineux, tels que 2G ou plus, les flux de fichiers ne fonctionneront pas, vous devez donc utiliser les méthodes liées au mappage de mémoire de l'API, même. s'il s'agit d'un mappage de mémoire, la taille entière du fichier ne peut pas non plus être mappée en une seule fois, le mappage doit donc être effectué par morceaux, en traitant une petite partie à la fois.
Voyons d'abord quelques fonctions
CreateFile : ouvrir un fichier
GetFileSize : obtenir la taille du fichier
CreateFileMapping : créer un mappage
MapViewOfFile : fichier de mappage
En regardant l'aide de MapViewOfFile, ses deux derniers paramètres doivent être un multiple entier de la granularité de la page. Généralement, la granularité de la page de la machine est de 64 Ko (65 536 octets). Cependant, dans notre fonctionnement réel, ce n'est généralement pas le cas. . N'importe quelle position, n'importe quelle longueur est possible, donc un certain traitement doit être effectué.
La tâche de cet exemple est de lire séquentiellement les valeurs de longueur à partir d'une liste de longueurs (FInfoList), puis de lire séquentiellement les données de la longueur spécifiée à partir d'un autre gros fichier (FSourceFileName). S'il s'agit d'un petit fichier, c'est plus facile. Il suffit de lire le flux du fichier une fois, puis de le lire dans l'ordre. Pour les fichiers volumineux, nous devons constamment modifier la position du mappage pour obtenir les données souhaitées.
Cet exemple montre que la granularité de la page est d'abord obtenue via GetSystemInfo, puis 10 fois la granularité de la page est utilisée comme bloc de données mappé. Dans la boucle for, il sera jugé si la longueur lue (totale) plus la longueur à lire. sont dans cette fois Dans la plage de mappage (10 fois la granularité de la page), si c'est le cas, continuez la lecture. Si elle est dépassée, les données restantes doivent être enregistrées, puis le bloc de mémoire suivant doit être remappé et les données restantes enregistrées doivent être fusionnées. dans la nouvelle lecture Les données obtenues sont un peu alambiquées (peut-être que mon idée est trop alambiquée),
Le code est répertorié ci-dessous.
procédure TGetDataThread.DoGetData; var FFile_Handle; FFile_Map:THandle; list:TStringList; p:PChar:Integer; TMemoryStream.Create; liste := TStringList.Create; //Obtenir les informations système GetSystemInfo(sysinfo); //Granularité d'allocation de page blocksize := sysinfo.dwAllocationGranularity; //Ouvrir le fichier FFile_Handle := CreateFile(PChar(FSourceFileName),GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0); FFile_Handle = INVALID_HANDLE_VALUE then Exit ; //Obtenir la taille du fichier filesize := GetFileSize(FFile_Handle,nil); //Créer le mappage FFile_Map := CreateFileMapping(FFile_Handle,nil,PAGE_READONLY,0,0,nil); //Ici, nous avons mappé 10 fois la taille du bloc en un seul bloc de données. Si la taille du fichier est inférieure à 10 fois la taille du bloc, la longueur entière du fichier sera mappée directement si la taille du fichier div blocksize > 10 puis readlen := 10*blocksize sinon readlen := taille du fichier ; pour i := 0 à FInfoList.Count - 1 do start list.Delimiter := ':' list.DelimitedText := FInfoList.Strings[i]; //Récupérez la longueur, j'ai fait l'analyse ici, car les informations que j'ai stockées sont de type a:b:c, donc len est séparé par ::= StrToInt(list.Strings[1]); .Strings[2]); si (i = 0) ou (totallen+len >=readlen) alors commencez //Si la longueur lue plus la longueur à lire est supérieure à 10 fois la taille du bloc, alors nous devons conserver le contenu à la fin du mappage précédent afin de le fusionner avec le nouveau contenu du mappage si i > 0 alors commencez offset := offset + readlen ; //Écrit dans le flux temporaire tstream.Write(p^,readlen-totallen); tstream.Position := 0; //Si la longueur des données non lues n'est pas suffisante pour une allocation. granularité, puis mappez directement la longueur restante si filesize-offset <blockize then readlen := filesize-offset; //Mapping, p est un pointeur vers la zone de mappage //Notez que le troisième paramètre ici est toujours défini sur 0. Cette valeur doit être définie en fonction de la situation réelle p:= PChar(MapViewOfFile( FFile_Map,FILE_MAP_READ, 0,offset,readlen)); end; // S'il y a des données dans le flux temporaire, elles doivent être fusionnées si tstream.Size > 0 alors commencez // Copiez les données du flux temporaire sur stream.CopyFrom(tstream,tstream.Size); //Ensuite, écrivez de nouvelles données à la fin et fusionnez pour terminer stream.Write(p^,len-tstream.Size); - tstream.Size; //Déplace la position du pointeur pour pointer vers le début de la donnée suivante Inc(p,len-tstream.Size); tstream.Clear; end else start stream.Write(p^,len); total := total + len; Inc(p,len); end; stream.Position := 0; //Enregistre le flux dans un fichier stream.SaveToFile(IntToStr(i)+'.txt'); Free ; tstream.Free; CloseHandle(FFile_Handle); CloseHandle(FFile_Map);
Si vous avez des questions, n'hésitez pas à laisser un message ou à vous rendre dans la communauté de ce site pour communiquer et discuter. Merci d'avoir lu, j'espère que cela pourra aider tout le monde. Merci pour votre soutien à ce site !