![]() |
Datei im Speicher zeilenweise lesen
Ich lade so:
Delphi-Quellcode:
eine Datei in den Speicher. Es handelt sich dabei um eine Textdatei, die ich nun zeilenweise lesen muss. Wie kann ich das nun am einfachsten bewerkstelliegen ohne jedes Byte einzeln zu lesen und nach einem Zeilenumbruch zu suchen? Wichjtig: Es muss mit WinAPI Funktionen geschehen, das heißt eine StringListe oder ähnliches kann ich nicht benutzen.
var
hMemory : THandle; pMemory : pointer; begin hFile := CreateFile(szFilename,GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ or FILE_SHARE_WRITE,nil,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,0); // Speicher anfordern, & Dateiinhalt lesen hMemory := GlobalAlloc(GMEM_MOVEABLE or GMEM_ZEROINIT,MEMSIZE); pMemory := GlobalLock (hMemory); ReadFile(hFile,pMemory^,MEMSIZE-1,SizeReadWrite,nil); Hintergrund ist der, dass die Datei auch größer sein kann und ich nicht für jede Zeile lesend auf den Datenträger zugreifen will, da dies zum einen sehr langsam ist, das Betriebssystem sonst andauernd mit lesen vom Datenträger beschäftigt wäre und bei Notebooks kommt hinzu, dass der dauernde Zugriff auf den Datenträger sehr viel Strom verbraucht. Deshalb kann ich auch nicht mit AssignFile und Readln arbeiten. |
Re: Datei im Speicher zeilenweise lesen
Hi Luckie,
lies es doch gleich in eine verkettete Liste Zeilenweise ein. mfg BrunoT |
Re: Datei im Speicher zeilenweise lesen
Um es dir einfach zu machen, könntest du wirklich einfach ein Text bzw. TextFile mit ReadLn benutzen. Die Puffergröße kannst du mit System.SetTextBuf vergrößern (z.B. auf 64K). Das sollte die Datenträgerzugriffe auf ein Minimum beschränken.
Ansonsten müsstest du dir wirklich jedes Zeilenende selbst suchen. Würde natürlich zur Not auch gehen, indem du direkt einen String als Puffer nimmst, also etwa so (ungetestet):
Delphi-Quellcode:
const
BUFSIZE = 65536; // Initialisierung function InitLine(hFile: THandle; var FileBufStr: string): boolean; var GotBytes: cardinal; begin SetLength(FileBufStr, BUFSIZE); Result := ReadFile(hFile, @FileBufStr[1], BUFSIZE, GotBytes, nil); if Result then SetLength(FileBufStr, GotBytes) else FileBufStr := ''; end; // Zeile lesen nach Output, liefert FALSE bei EOF, sonst TRUE function GetLine(hFile: THandle; var FileBufStr: string; var Output: string): boolean; var P, OldLen: integer; GotBytes: cardinal; begin P := Pos(#10, FileBufStr); while P = 0 do begin OldLen := Length(FileBufStr); SetLength(FileBufStr, OldLen + BUFSIZE); if not ReadFile(hFile, @FileBufStr[OldLen], BUFSIZE, GotBytes, nil) then break; if GotBytes = 0 then break; SetLength(FileBufStr, OldLen + GotBytes); P := Pos(#10, FileBufStr); end; if P > 0 then begin Result := true; Output := Copy(FileBufStr, 1, P - 1); Delete(FileBufStr, 1, P); end else begin Result := FileBufStr <> ''; Output := FileBufStr; FileBufStr := ''; end; end; |
Re: Datei im Speicher zeilenweise lesen
die c standardfunktionen fuer zeilenweises lesen lesen auch nur zeichen fuer zeichen.
du koenntest das aber optimieren, wenn du ein bisschen mit zeigern arbeitest. zeiger auf den anfang setzen und dann immer weitergehen bis ein umbruch kommt. dann von anfang bis hier als zeile betrachten. dann das gleiche spiel nochmal. |
Re: Datei im Speicher zeilenweise lesen
Entschuldigt mal bitte, was soll den dieses 'lesen auch nur Zeichen für Zeichen'. Das macht doch keine Funktion mehr: Windows sorgt dafür, das die Daten blockweise eingelesen werden (üblicherweise in Größe der Systempage, also 8kb). Das ist so ziemlich optimal. Wenn ich dann bis zum nächsten Zeilenende lesen will, dann bleibt mir ja wohl nichts anderes übrig, als Zeichen für Zeichen zu vergleichen. Windows sorgt z.B. bei einem Stream schon dafür, das das recht flott geht.
Damit deine SW stabil bleibt, musst Du eine kleine Mittelschicht implementieren, die aus Windows-Seiten einzelne Zeilen macht. Wenn Du weisst, das eine Zeile maximal 1000 Zeichen lang ist, dann hast Du zwei Buffer a 1000 Zeilen, die Du abwechselnd nachliest: Wenn Buffer A 'fast' am Ende ist, lässt Du die folgende Seite in Buffer 'B' einlesen und umgekehrt. Die beiden Buffer liegen im Speicher aber direkt hintereinander, sodass man A und B als ein Array implementiert. BufferA zeigt dann z.B. auf Array[0] und BufferB auf Array[1000]. Du hast dann noch eine (Inline-) Funktion GetChar, die dir Array[X] zurückliefert, X erhöht und ggf A bzw. B nachlädt. Das wars. Du merkst Dir also 'X', liest per GetChar bis zum nächsten CR/LF und kopierst Array [Xstart]...Array [X-2] in dein Resultatstring. Dabei musst Du nur darauf achten, ob X<XStart ist, dann sind es nämlich zwei Move-Operationen, ansonsten nur eine. |
Re: Datei im Speicher zeilenweise lesen
Liste der Anhänge anzeigen (Anzahl: 2)
Hi Luckie,
ich will mal für meinen Lösungsvoeschlag ein Beispiel anhängen:
Delphi-Quellcode:
///Der einzige "Nachteil" ist, dass die Textdatei von hinten nach vorn im Speicher liegt.
unit Unit1;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; Memo1: TMemo; Button2: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); private { Private-Deklarationen } public { Public-Deklarationen } end; var Form1: TForm1; implementation {$R *.dfm} type Zeiger = ^tZeiger; tZeiger = record Nr:Integer; Str: string; Ptr: zeiger; end; var StartZ, HilfPtr1,HilfPtr2: Zeiger; procedure in_Liste(i:Integer;s: string); begin New(HilfPtr1); if i=0 then Begin New(HilfPtr2); StartZ:=HilfPtr2; end; HilfPtr1^.Nr := i; HilfPtr1^.Str := s; HilfPtr2^.Ptr := HilfPtr1; HilfPtr2:=HilfPtr1; end; procedure TForm1.Button1Click(Sender: TObject); var T: Textfile; s: string; i:integer; begin i:=0; memo1.Clear; Assignfile(t, 'test.txt'); reset(t); while not eof(t) do begin readln(t, s); in_Liste(i,s); inc(i); end; closefile(t); end; procedure TForm1.Button2Click(Sender: TObject); begin HilfPtr1 := StartZ; while (HilfPtr1 <> nil) do begin HilfPtr1 := HilfPtr1^.Ptr; if HilfPtr1 <> nil then Memo1.Lines.Add(HilfPtr1.Str); end; end; end. [edit] ich habe es jetzt umgedreht. Die Daten liegen jetzt in der richtigen Reihenfolge [/edit] :mrgreen: //Aber das lässt sich ja ändern. mfg BrunoT |
Re: Datei im Speicher zeilenweise lesen
Micha, es ist völlig in Ordnung wenn du zeichenweise einliest. Wie oben erwähnt, tun dies andere Libs auch. Damit wird die Performance nicht wirklich gesenkt ;)
|
Re: Datei im Speicher zeilenweise lesen
Zitat:
|
Re: Datei im Speicher zeilenweise lesen
Hallo Luckie,
schau dir mal das an: FileMapping: ![]() evtl. ist das interessant für dich. |
Re: Datei im Speicher zeilenweise lesen
Zitat:
Ich gehe durch den Puffer und ersetze alle #13 durch #0, danach habe ich ein typisches "Array" von nullterminierten Strings und muß ähnliche Methoden anwenden wie bei REG_MULTI_SZ. Wenn der Puffer R/O ist, dann würde ich selbstverständlich nur nach #13 suchen ohne zu ersetzen und dann eben die entsprechenden Anfangsoffsets und Endoffsets merken und mit lstrcpyn() kopieren. Was du keinesfalls machen solltest ist, einen String zu nehmen und die Zeichen jeweils zeichenweise an den String anzuhängen. Dadurch würde intern viel mehr ablaufen als das einfach Kopieren einer Zeile in einen String oder Puffer. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 09:52 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz