Die Verwendung von Speicherzuordnung für große Dateien in Delphi
Da die Anwendung nicht kompliziert ist, kann es sein, dass ich auf eine solche Anforderung stoße.
Bei einigen kleinen Dateien können normale Dateistreams zur Lösung des Problems verwendet werden. Bei sehr großen Dateien, z. B. 2G oder mehr, funktionieren Dateistreams jedoch nicht, sodass Sie sogar die Speicherzuordnungsmethoden der API verwenden müssen Wenn es sich um eine Speicherzuordnung handelt, kann nicht die gesamte Dateigröße auf einmal zugeordnet werden, daher muss die Zuordnung in Blöcken erfolgen und jeweils ein kleiner Teil verarbeitet werden.
Schauen wir uns zunächst einige Funktionen an
CreateFile: eine Datei öffnen
GetFileSize: Dateigröße abrufen
CreateFileMapping: Zuordnung erstellen
MapViewOfFile: Zuordnungsdatei
Betrachtet man die Hilfe von MapViewOfFile, müssen die letzten beiden Parameter ein ganzzahliges Vielfaches der Seitengranularität der Maschine sein. In unserem tatsächlichen Betrieb ist dies jedoch im Allgemeinen nicht der Fall . Jede Position, jede beliebige Länge ist möglich, daher muss eine gewisse Verarbeitung durchgeführt werden.
Die Aufgabe dieses Beispiels besteht darin, die Längenwerte nacheinander aus einer Längenliste (FInfoList) zu lesen und dann die Daten der angegebenen Länge nacheinander aus einer anderen großen Datei (FSourceFileName) zu lesen. Wenn es sich um eine kleine Datei handelt, ist dies einfacher Zur Handhabung reicht es aus, den Dateistrom einmal zu lesen und ihn dann nacheinander zu lesen. Bei großen Dateien müssen wir die Zuordnungsposition ständig ändern, um die gewünschten Daten zu erhalten.
Dieses Beispiel zeigt, dass die Seitengranularität zuerst über GetSystemInfo ermittelt wird und dann das Zehnfache der Seitengranularität als zugeordneter Datenblock verwendet wird. In der for-Schleife wird beurteilt, ob die gelesene Länge (gesamt) plus die zu lesende Länge ist sind innerhalb dieser Zeit Wenn der Zuordnungsbereich (10-fache Seitengranularität) liegt, lesen Sie weiter. Wenn er überschritten wird, müssen die verbleibenden Daten aufgezeichnet werden, und dann muss der nächste Speicherblock neu zugeordnet und die aufgezeichneten verbleibenden Daten zusammengeführt werden In den neuen Lesevorgang sind die erhaltenen Daten etwas kompliziert (vielleicht ist meine Idee zu kompliziert),
Der Code ist unten aufgeführt.
procedure TGetDataThread.DoGetData; var FFile_Map:THandle; p:PChar; offset := TMemoryStream.Create; TMemoryStream.Create; list := TStringList.Create; //Systeminformationen abrufen GetSystemInfo(sysinfo); //Seitenzuordnungsgranularität blocksize := sysinfo.dwAllocationGranularity; //Datei öffnen FFile_Handle := CreateFile(PChar(FSourceFileName),GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0); FFile_Handle = INVALID_HANDLE_VALUE then Exit; //Dateigröße abrufen filesize := GetFileSize(FFile_Handle,nil); //Mapping erstellen FFile_Map := CreateFileMapping(FFile_Handle,nil,PAGE_READONLY,0,0,nil); //Hier haben wir die 10-fache Blockgröße einem Datenblock zugeordnet. Wenn die Dateigröße kleiner als das 10-fache der Blockgröße ist, wird die gesamte Dateilänge direkt zugeordnet, wenn die Dateigröße div Blocksize > 10 ist, dann readlen := 10*blocksize else readlen := filesize; for i := 0 to FInfoList.Count - 1 do begin list.Delimiter := ':' list.DelimitedText := FInfoList.Strings[i]; //Ermitteln Sie die Länge, ich habe die Analyse hier durchgeführt, da die von mir gespeicherten Informationen vom Typ a:b:c sind, sodass len durch ::= StrToInt(list.Strings[1]); .Strings[2]); if (i = 0) or (totallen+len >=readlen) then begin //Wenn die Leselänge plus die zu lesende Länge größer als das Zehnfache der Blockgröße ist, müssen wir den Inhalt am Ende der vorherigen Zuordnung beibehalten, um ihn mit dem neuen Zuordnungsinhalt zusammenzuführen. Wenn i > 0, dann beginnen offset := offset + readlen ; //In den temporären Stream schreiben tstream.Write(p^,readlen-totallen); //Wenn die Länge der ungelesenen Daten nicht für eine Zuweisung ausreicht Granularität, dann direkt die verbleibende Länge zuordnen, wenn dann Dateigrößen-Offset < Blockgröße readlen := filesize-offset; //Mapping, p ist ein Zeiger auf den Mapping-Bereich //Beachten Sie, dass der dritte Parameter hier immer auf 0 gesetzt ist. Dieser Wert sollte entsprechend der tatsächlichen Situation eingestellt werden p:= PChar(MapViewOfFile( FFile_Map,FILE_MAP_READ, 0,offset,readlen)); //Wenn Daten im temporären Stream vorhanden sind, müssen diese zusammengeführt werden, wenn tstream.Size > 0 dann begin //Kopieren Sie die temporären Stream-Daten über stream.CopyFrom(tstream,tstream.Size); //Schreiben Sie dann am Ende neue Daten und führen Sie sie zusammen, um stream.Write(p^,len-tstream.Size); totallen := len - tstream.Size; //Position des Zeigers so verschieben, dass er auf den Anfang der nächsten Daten zeigt Inc(p,len-tstream.Size); totallen := totallen + len; Inc(p,len); stream.Position := 0; //Speichern Sie den Stream in einer Datei stream.SaveToFile(i)+'.txt'; Free ; tstream.Free; CloseHandle(FFile_Map);
Wenn Sie Fragen haben, hinterlassen Sie bitte eine Nachricht oder gehen Sie zur Community dieser Website, um zu kommunizieren und zu diskutieren. Ich hoffe, dass es allen helfen kann.