| 
| Autor | Beitrag |  
| Flamefire 
          Beiträge: 1207
 Erhaltene Danke: 31
 
 Win 10
 Delphi 2009 Pro, C++ (Visual Studio)
 
 | 
Verfasst: Sa 05.06.10 12:34 
 
Ich will einen String aus einer strukturierten Datei lesen. Der hat eine Variable länge (0-x) und endet mit einer 0.
 		                       Delphi-Quelltext 
 									| 1:2:
 3:
 4:
 5:
 6:
 7:
 8:
 9:
 
 | function ReadString(const hFile: THandle):String;var c:AnsiChar;
 begin
 Result:='';
 repeat
 if(not ReadF(hFile,c,1) or (c=#0)) then break;
 Result:=Result+String(c);
 until false;
 end;
 |  geht zwar, ist aber ineffektiv.
 Wenn ich mehr auf einmal lese, muss ich aber den FilePointer umsetzen u.ä.
 Was wäre die beste Lösung für das Problem? |  |  |  
| Luckie Ehemaliges Mitglied
 Erhaltene Danke: 1
 
 
 
 
 | 
Verfasst: Sa 05.06.10 17:51 
 
Den FilePointer setzen macht Windows auch, sonst könnte man ja die Datei nicht lesen. Generell ist es performanter in größeren Stücken zu lesen und dann, in deinem Fall, die Zeichen zu zerlegen. |  |  |  
| jaenicke 
          Beiträge: 19326
 Erhaltene Danke: 1749
 
 W11 x64 (Chrome, Edge)
 Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
 
 | 
Verfasst: So 06.06.10 00:04 
 
Ich löse das ganz gerne mit MMFs, damit lassen sich viele Millionen Buchstaben pro Sekunde aus einer Datei zeilenweise auslesen. Inklusive Findung des Zeilenumbruchs usw., genauso kannst du dann das Nullzeichen suchen.
 Du findest eine entsprechende Unit in meinem Registryeditor:
www.delphi-forum.de/....php?p=567859#567859 Interessant ist dabei die Datei Source\Units\FileAccessUtils.pas, in der ich das umgesetzt habe. Der Quelltext sieht so aus: 												| 1:2:
 3:
 4:
 5:
 6:
 7:
 8:
 9:
 10:
 11:
 12:
 13:
 14:
 15:
 16:
 17:
 18:
 19:
 20:
 21:
 22:
 23:
 24:
 25:
 26:
 27:
 28:
 29:
 30:
 31:
 32:
 33:
 34:
 35:
 36:
 37:
 38:
 39:
 40:
 41:
 42:
 43:
 44:
 45:
 46:
 47:
 48:
 49:
 50:
 51:
 52:
 53:
 54:
 55:
 56:
 57:
 58:
 59:
 60:
 61:
 62:
 63:
 64:
 65:
 66:
 67:
 68:
 69:
 70:
 71:
 72:
 73:
 74:
 75:
 76:
 77:
 78:
 79:
 80:
 81:
 82:
 83:
 84:
 85:
 86:
 87:
 88:
 89:
 90:
 91:
 92:
 93:
 94:
 95:
 96:
 97:
 98:
 99:
 100:
 101:
 102:
 103:
 104:
 105:
 106:
 107:
 108:
 109:
 110:
 111:
 112:
 113:
 114:
 115:
 116:
 117:
 118:
 119:
 120:
 121:
 122:
 123:
 124:
 125:
 126:
 127:
 128:
 129:
 130:
 131:
 132:
 133:
 134:
 135:
 136:
 137:
 138:
 139:
 140:
 141:
 142:
 143:
 144:
 145:
 146:
 147:
 148:
 149:
 150:
 151:
 152:
 153:
 154:
 155:
 156:
 157:
 158:
 159:
 160:
 161:
 162:
 
 | ...
 type
 TFileReader = class
 private
 FPointer: Pointer;
 FFile, FMapping: THandle;
 FSize, FBufferPos, FPosInBuffer: Int64;
 FBuffer, FAllocationGranularity: Cardinal;
 FUnicodeFile: Boolean;
 function GetPosition: Int64;
 procedure SetBuffer(const Value: Cardinal);
 procedure SetPosition(const Value: Int64);
 procedure ReInitView;
 public
 constructor Create(AFileName: string; AInit: Boolean = True);
 destructor Destroy; override;
 procedure ReadLn(var AValue: string);
 property Size: Int64 read FSize;
 property Buffer: Cardinal read FBuffer write SetBuffer;
 property Position: Int64 read GetPosition write SetPosition;
 end;
 
 implementation
 
 
 
 constructor TFileReader.Create(AFileName: string; AInit: Boolean = True);
 var
 SysInfo: _SYSTEM_INFO;
 begin
 FFile := CreateFile(PChar(AFileName), GENERIC_READ, FILE_SHARE_READ, nil,
 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
 if FFile = 0 then
 raise Exception.Create('')
 else
 begin
 FSize := FileSeek(FFile, Int64(0), Ord(soEnd));
 FMapping := CreateFileMapping(FFile, nil, PAGE_READONLY, 0, 0, nil);
 end;
 GetSystemInfo(SysInfo);
 FAllocationGranularity := SysInfo.dwAllocationGranularity;
 FBufferPos := 0;
 if FSize >= 224 * FAllocationGranularity then
 FBuffer := 224 * FAllocationGranularity
 else if FSize >= 16 * FAllocationGranularity then
 FBuffer := 16 * FAllocationGranularity
 else if FSize >= FAllocationGranularity then
 FBuffer := FAllocationGranularity
 else
 FBuffer := FSize;
 FPosInBuffer := 0;
 if AInit then
 ReInitView;
 end;
 
 destructor TFileReader.Destroy;
 begin
 CloseHandle(FFile);
 inherited;
 end;
 
 procedure TFileReader.ReInitView;
 begin
 if Assigned(FPointer) then
 UnmapViewOfFile(FPointer);
 FPointer := MapViewOfFile(FMapping, FILE_MAP_READ, Int64Rec(FBufferPos).Hi,
 Int64Rec(FBufferPos).Lo, FBuffer);
 if FBufferPos = 0 then
 FUnicodeFile := (FSize > SizeOf(Word)) and (PWord(FPointer)^ = $FEFF);
 end;
 
 procedure TFileReader.SetBuffer(const Value: Cardinal);
 begin
 FBuffer := Succ(Value div FAllocationGranularity) * FAllocationGranularity;
 ReInitView;
 end;
 
 function TFileReader.GetPosition: Int64;
 begin
 Result := FBufferPos + FPosInBuffer;
 end;
 
 procedure TFileReader.SetPosition(const Value: Int64);
 begin
 if (Value < FBufferPos) or (Value >= FBufferPos + FBuffer) then
 begin
 FBufferPos := Value - (Value mod FAllocationGranularity);
 if FSize < FBufferPos + FBuffer then
 FBuffer := FSize - FBufferPos;
 FPosInBuffer := Value mod FAllocationGranularity;
 end
 else
 FPosInBuffer := Value - FBufferPos;
 ReInitView;
 end;
 
 procedure TFileReader.ReadLn(var AValue: string);
 var
 LineLen: Cardinal;
 CurValue: PByte;
 CurData, StartPointer, EndPointer: Pointer;
 RemainingLine: string;
 LineEndFound: Boolean;
 i: Integer;
 begin
 if not Assigned(FPointer) then
 Exit;
 StartPointer := Pointer(Integer(FPointer) + FPosInBuffer);
 EndPointer := Pointer(Integer(FPointer) + Integer(FBuffer));
 CurData := StartPointer;
 for i := Integer(EndPointer) - Integer(CurData) - 1 downto 0 do
 if PByte(CurData)^ = $0D then
 Break
 else
 Inc(PByte(CurData));
 
 LineLen := Integer(CurData) - Integer(StartPointer);
 if FUnicodeFile then
 begin
 if (FBufferPos = 0) and (FPosInBuffer = 0) then
 begin
 Inc(PByte(StartPointer), 2);
 SetString(AValue, nil, LineLen div 2 - 1);
 end
 else;
 SetString(AValue, nil, LineLen div 2);
 if Length(AValue) > 0 then
 begin
 CurValue := PByte(AValue);
 while Integer(StartPointer) < Integer(CurData) do
 begin
 CurValue^ := PByte(StartPointer)^;
 Inc(PByte(StartPointer), 2);
 Inc(CurValue);
 end;
 end;
 Inc(FPosInBuffer, 2);
 end
 else
 begin
 SetString(AValue, nil, LineLen);
 CopyMemory(PChar(AValue), StartPointer, LineLen);
 end;
 Inc(FPosInBuffer, LineLen + 2);
 if Integer(CurData) = Integer(EndPointer) then
 begin
 LineEndFound := PByte(Pred(Integer(CurData)))^ = $0D;
 Position := FBufferPos + FBuffer;
 if not LineEndFound and (Position < Size) then
 begin
 ReadLn(RemainingLine);
 AValue := AValue + RemainingLine;
 end;
 end;
 end;
 |  Du musst eben statt nach $0D nach dem Nullzeichen suchen. |  |  |  
| Flamefire  
          Beiträge: 1207
 Erhaltene Danke: 31
 
 Win 10
 Delphi 2009 Pro, C++ (Visual Studio)
 
 | 
Verfasst: So 06.06.10 01:18 
 
Wenn ich das richtig verstanden habe, dann mappt MapViewOfFile nur Fbuffer bytes der Datei ab einer bestimmten Position.
 am Anfang legst du es auf eine bestimmte Größe fest, die kleiner als die Datei sein kann. Ich würde also nicht alles lesen können, wenn z.b. meine struktur über diese größe drüber raus geht, oder?
 Auch dürfte es da einen netten Bug geben: position auf ende setzen, dann wieder auf anfang: Fbuffer ist sehr klein (0?)
 In einem ähnlichem Thread hier  wurde gphugefile empfohlen, dürfte das nicht auch hier das Problem lösen?
 BTW: Interessehalber: warum hast du die Faktoren auf 16 und 224 gesetzt, und nicht 256? |  |  |  
| BenBE 
          Beiträge: 8721
 Erhaltene Danke: 191
 
 Win95, Win98SE, Win2K, WinXP
 D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
 
 | 
Verfasst: So 06.06.10 01:35 
 
Die 16 ergibt bei 4K-Pages 64KB an Speicher. Das ist für 16 Bit die maximal adressierbare Speichermenge. Unter 32-Bit-Systemen ist das aber witziger Weise ein extrem guter Kompromiss zum Lesen von Datenblöcken, weil für die meisten Medien (Festplatten, Netzwerk, ...) 64K-Blöcke als obere Grenze recht effizient geholt werden können, ohne zu große Latenzen zu erzeugen.
 Die 224 vs. 256 dürfte mit der Vermeidung von zu großen Speicherblöcken auf dem Heap zu tun haben, da dieser für kleine Blöcke optimiert ist. Da bin ich mir aber nicht ganz sicher.
 _________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
 |  |  |  
| jaenicke 
          Beiträge: 19326
 Erhaltene Danke: 1749
 
 W11 x64 (Chrome, Edge)
 Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
 
 | 
Verfasst: So 06.06.10 09:28 
 
	  |  Flamefire hat folgendes geschrieben  : |  	  | Ich würde also nicht alles lesen können, wenn z.b. meine struktur über diese größe drüber raus geht, oder? | 
 Doch, dann wird weitergelesen. Das ist Zeile 152 und folgende.
 Mit LineEndFound wird dort geprüft ob ein Zeilenende erreicht wurde. Wenn nicht und die Position noch kleiner als die verfügbare Größe ist, dann wird erneut ReadLn angestoßen und einfach weitergelesen. Bis eben ein Zeilenumbruch erreicht oder alles eingelesen ist.
 An der Stelle ließe sich auch weiter optimieren, aber da die Stringkonkatenationen in Delphi schon gut optimiert sind und der Fall bei einer guten Buffergröße nicht so oft eintreten wird, habe ich das nicht weiter verfolgt.
 	  |  Flamefire hat folgendes geschrieben  : |  	  | Auch dürfte es da einen netten Bug geben: position auf ende setzen, dann wieder auf anfang: Fbuffer ist sehr klein (0?) | 
 Das stimmt, das prüfe ich gar nicht.      Ich glaube es ist sinnvoll eine temporäre Buffergröße einzuführen. Wenn die Buffergröße nicht eingehalten werden kann, wird nur diese geändert. Dann lässt sich bei der nächsten Änderung der Position das wieder auf den eigentlich gewünschten Wert setzen. Das schlägt zwei Fliegen mit einer Klappe.    Ich werde mich da mal kurz ransetzen und erstens eine kleine Demo schreiben und zweitens das ganze etwas verallgemeinern (z.B. gesuchtes Endzeichen festlegbar machen).
 Mit dieser Demo werde ich das dann einmal separat in Open Source veröffentlichen. Sollte inklusive Eintrag in die interne Projekt- und Buildverwaltung nicht allzu lange dauern, ich rechne einmal mit 11:30-12:00 Uhr.
 // EDIT:
 Na gut, ich hatte noch etwas anderes zu tun, aber hier gibts das jetzt, korrigiert und mit Demo usw.:
www.delphi-forum.de/....php?p=607865#607865 |  |  |  
| Flamefire  
          Beiträge: 1207
 Erhaltene Danke: 31
 
 Win 10
 Delphi 2009 Pro, C++ (Visual Studio)
 
 | 
Verfasst: So 06.06.10 21:11 
 
@jeanicke: und dein statement zu den 224?    was ich aber noch bräuchte wären ReadOperationen für beliebige Daten und entsprechende Schreiboperationen.
 Da ich mich mit MMF nicht auskenne kurze Frage: Es dürfte doch reichen, in den Speicher zu schreiben von dem auch gelesen wird, oder? Wie sieht es da mit der Größe aus? Bei MMF müsste doch die Größe schon vorher feststehen, was sie für mich ungeeignet macht. Denn ich schreibe ja mehr oder weniger sequentiell Daten aus einer Struktur da rein. |  |  |  
| jaenicke 
          Beiträge: 19326
 Erhaltene Danke: 1749
 
 W11 x64 (Chrome, Edge)
 Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
 
 | 
Verfasst: So 06.06.10 21:21 
 
	  |  Flamefire hat folgendes geschrieben  : |  	  | @jeanicke: und dein statement zu den 224?  | 
 Ich kann leider gar nicht weiter was dazu sagen, ich habe mit verschiedenen Werten experimentiert und damit ging es am schnellsten.    Etwas bestimmtes dabei gedacht habe ich mir also gar nicht.    // EDIT:
 Was das Schreiben in die Datei angeht, siehe den entsprechenden Thread, ich habe die Antwort einmal dorthin verlegt:
www.delphi-forum.de/....php?p=607948#607948 |  |  |  |