Mega Code Archive

 
Categories / Delphi / Files
 

TMapTextfile Class

Title: TMapTextfile Class Question: In Delphi you have many ways to manipulate text files. But all of these have a handicap: You must read the whole file (StringList) or you can only access data sequential (Assign/Readln). Answer: The Windows API provides the so called Memory Mapped Files (MMF). Windows self uses MMFs to load EXE or DLL. Therefore the mechanism is very efficient, but simple to handle. The only handicap is, that you must know the size of the file before you access it. This means for typical text files, that the operation is normally limited to read from the file or manipulate inside. The class TMapTextfile wraps the neccesary API calls and povides efficent functions for accessing the data. Installation: Copy MAPTEXTFILE.PAS into your library path. Use it. 8-) Tip for using this class under Delphi 3: Note that Delphi has changend the definition of CARDINAL from D3 to D4. In D3 CARDINAL is a subset of INTEGER (0..MaxInt - 31 Bit wide), in D4 CARDINAL is the full range (32 bit wide). TMapTextfiles provides the following properties and methods: Create Creates an instance of the class Destroy Destroys the instance Open Opens a file as a memory mapped file. filename = name of the file mode = open mode: mmRead = Open only for read exclusive mmReadShared = Open for read, share with other mmReadWrite = Open for read and wrie Returns INVALID_HANDLE_VALUE if the file not exist.If the result 0 all is OK. NOTE: 1. The file must exist! 2. You cannot write behind the end of the file. 3. You cannot write behind the end of the file.8-) Close Close the memory mapped file and frees all handles. EndOfFile Returns TRUE, if the End of the file is reached. GetSize Returns the Size of the file in Bytes GetPos Returns the actual file read/write position NOTE: Position 0 is the start of the file SetPos Sets the actual read/write position NOTE: Position 0 is the start of the file ReadChar Reads a character from the actual read/write position. The r/w position is incremented. ReadString Returns a string, starting at the actual r/w pos. The string is delimited by chars by ESC (#27) and TAB. The r/w position moves to the end of the string, delimiter chararcters (Cr/LF) are skipped. ReadStringL Same as ReadString, but return length of the string len = number of characters that we has read ReadLn Same as ReadSring, but like the classic Procedure. ReadCharAt Reads a charater from an arbitray position. The r/w position is NOT moved! pos = position to read from (0 = start of file!) ReadChars Reads a line of charactes from the MMF. The r/w position is NOT moved! str = The buffer to read to pos = position to read from (0 = Start of file!) len = number of characters to read. ReadStringAt Returns a string, starting at an arbitray pos. The string is delimited by characters but not by ESC (#27) and TAB. The r/w position is NOT moved. FindString Findes a substring in the MMF and Returns the pos. str = the substring to search for pos = position to start the search (0 = start of the file) max = position to end the search. If this is 0 or less then 0 the end of the file is the limit. Returns the position of the substring or -1 if the substring is not found. FindStringCI Same as FindString, but works case insensitive FindWildCard Same as FindString, but supports wildcard search. str = the substring to search for pos = position to start the search (0 = start of the file) max = position to end the search. If this is 0 or less then end of file is the limit. wild = the character used as wildcard (i.e. "*") joker = the character used as joker (i.e. "?") Returns the position of the substring or -1 if the substring is not found. ReadBytes Reads a number of bytes to a anonymous variable. The r/w position is NOT moved! b = the anonymous variable pos = position to read from (0 = start of the file) len = number of bytes to read. WriteBytes Writes a number of bytes to the file. NOTE: You can not write behind the end of file!!! b = the anonymous variable pos = position to write to (0 = start of the file) len = number of bytes to write GetPtr Returns the Position as Pointer (Pchar) to the view pos = Wanted position WARNING: Be carefull! Normally you dont need this! ----------------------------------------------------------------- unit MapTextfile; interface uses Classes, Windows; // Modes for open type tMapMode = (mmRead, mmReadShared, mmReadWrite); // the class type TMapTextfile = class private f_file: THandle; f_MMF: THandle; f_size: INTEGER; f_view: PByte; f_data: PChar; f_pos: INTEGER; f_open: BOOLEAN; function CalcPos (pos: INTEGER): PCHAR; function Find (const str: string; pos, max: integer; ci: boolean): INTEGER; public constructor Create; destructor Destroy; override; function Open (const filename: string; mode: tMapMode): INTEGER; procedure Close; function ReadChar: CHAR; function ReadString: string; function ReadStringL (var len: Longint): string; procedure ReadLn (var str: string); function ReadCharAt (pos: Longint): CHAR; procedure ReadChars (str: PCHAR; pos, len: LONGINT); function ReadStringAt (pos: Longint): string; function GetSize: Longint; function GetPos: Longint; procedure SetPos (pos: Longint); function EndOfFile: Boolean; function FindString (const str: string; pos, max: INTEGER): INTEGER; function FindStringCI (const str: string; pos, max: INTEGER): INTEGER; function FindWildCard (const str: string; pos, max: INTEGER; wild, joker: CHAR): INTEGER; procedure ReadBytes (var b; pos, len: LONGINT); procedure WriteBytes (var b; pos, len: LONGINT); function GetPtr (pos: Longint): PChar; end; implementation constructor TMapTextfile.Create; begin f_open:= FALSE; end; function TMapTextfile.Open (const filename: string; mode: tMapmode): INTEGER; var m1, m2, m3, m4: CARDINAL; begin f_open:= FALSE; case mode of mmRead: begin m1:= GENERIC_READ; m2:= PAGE_READONLY; m3:= FILE_MAP_READ; m4:= FILE_SHARE_READ or FILE_SHARE_WRITE; end; mmReadShared: begin m1:= GENERIC_READ; m2:= PAGE_READONLY; m3:= FILE_MAP_READ; m4:= FILE_SHARE_READ; end; else begin m1:= GENERIC_READ + GENERIC_WRITE; m2:= PAGE_READWRITE; m3:= FILE_MAP_WRITE; m4:= FILE_SHARE_READ; end; end; f_file:= CreateFile (PCHAR (FileName), m1, m4, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); // all ok? if f_file = INVALID_HANDLE_VALUE then begin Result:= Integer (INVALID_HANDLE_VALUE); EXIT; end; try // how big is the file? f_size:= GetFileSize (f_file, nil); // generate memory mapped file object f_MMF:= CreateFileMapping (f_file, nil, m2, 0, f_size, nil); // all ok? if f_MMF = 0 then begin Result:= -1; EXIT; end; finally // We need the file handle no longer, so we can free it. CloseHandle (f_file); end; // Status: file handle freed, MMF handle activ try // create a view (a "window" to the memory) to the MMF object f_View:= MapViewOfFile (f_MMF, m3, 0, 0, f_size); // all ok? if f_view = nil then begin Result:= -1; EXIT; end; finally // handle to the mmf object is no longer needed, so lets free it CloseHandle (f_MMF); end; f_data:= PCHAR (f_view); // Pointer to map view f_pos:= 0; // initial start position of the R/W-Pointer f_open:= True; // Yes, we're open for buisiness Result:= 0; // Status: file handle freed, mmf handle freed, view is active end; destructor TMapTextfile.Destroy; begin if f_open then Close; inherited; end; procedure TMapTextfile.Close; begin if f_open then begin UnmapViewOfFile (f_View); // Free the view f_open:= FALSE; end; end; function TMapTextfile.CalcPos (pos: INTEGER): PCHAR; // internal function for calculate read/write pos // to physical memory begin Result:= nil; if f_open then begin if pos if pos f_size then pos:= f_size; result:= PCHAR (LONGINT(f_view) + pos); end; end; function TMapTextfile.ReadChar: CHAR; // Read a char begin Result:= #0; if f_open then begin f_data:= PCHAR (LONGINT(f_view) + f_pos); Result:= f_data^; INC (f_pos); end; end; function TMapTextfile.ReadStringL (var len: Longint): string; begin Result:= ''; len:= 0; if f_open then begin f_data:= PCHAR (LONGINT(f_view) + f_pos); while (f_pos case f_data^ of #0..#31: case f_data^ of #9, #27: Result:= Result + f_data^; // including Tab and Escape #10: begin INC (f_pos); EXIT; end; // Linefeed terminates #13: begin // Carriage Return terminates INC (f_pos); INC (f_data); // is there a trailing Linefeed? if f_data^ = #10 then INC (f_pos); EXIT; end; end; else begin Inc (len); Result:= Result + f_data^; end; end; INC (f_pos); INC (f_data); end; end; end; function TMapTextfile.ReadString: string; begin Result:= ''; if f_open then begin f_data:= PCHAR (LONGINT(f_view) + f_pos); while (f_pos case f_data^ of #0..#31: case f_data^ of #9, #27: Result:= Result + f_data^; // including Tab and Escape #10: begin INC (f_pos); EXIT; end; // Linefeed terminates #13: begin // Carriage Return terminates INC (f_pos); INC (f_data); if f_data^ = #10 then INC (f_pos); EXIT; end; end; else begin Result:= Result + f_data^; end; end; INC (f_pos); INC (f_data); end; end; end; function TMapTextfile.ReadCharAt (pos: LONGINT): CHAR; begin if f_open then Result:= CalcPos (pos)^ else Result:= #0; end; procedure TMapTextfile.ReadChars (str: PCHAR; pos, len: LONGINT); var i: INTEGER; p: PCHAR; begin if f_open then begin if len i:= 0; p:= CalcPos (pos); while (i str^:= p^; INC (str); INC (p); INC (i); end; end; end; procedure TMapTextfile.ReadBytes (var b; pos, len: LONGINT); var p: PCHAR; begin if f_open then begin p:= CalcPos (pos); Move (p^, b, len); end; end; procedure TMapTextfile.WriteBytes (var b; pos, len: LONGINT); var p: PCHAR; begin if f_open then begin p:= CalcPos (pos); Move (b, p^, len); end; end; function TMapTextfile.ReadStringAt (pos: LONGINT): string; var i: INTEGER; p: PCHAR; begin Result:= ''; if f_open then begin p:= CalcPos (pos); i:= 0; while (i case p^ of #0..#31: case p^ of #9, #27: Result:= Result + p^; // including Tab and Escape #10, #13: EXIT; // Linefeed and Carriage Return terminates end; else Result:= Result + p^; end; INC (p); end; end; end; procedure TMapTextfile.ReadLn (var str: string); begin str:= ReadString; end; function TMapTextfile.GetSize: LONGINT; begin if f_open then Result:= f_size else Result:= -1; end; function TMapTextfile.GetPos: LONGINT; begin if f_open then Result:= f_pos else Result:= -1; end; procedure TMapTextfile.SetPos (pos: LONGINT); begin if f_open then begin if pos if pos f_size then pos:= f_size; f_pos:= pos; end; end; function TMapTextfile.EndOfFile: BOOLEAN; begin if f_open then Result:= f_pos = f_size else Result:= TRUE; end; function TMapTextfile.Find (const str: string; pos, max: integer; ci: BOOLEAN): INTEGER; // internal search function // str = string to find // pos = startpos // max = maximum length to search, -1 = until file end // ci = if true the Case insensitive var s, l1, j, k: INTEGER; p, x: PCHAR; begin Result:= -1; if f_open then begin if max if pos if pos max then EXIT; x:= PCHAR (str); p:= PCHAR (f_view); l1:= Length (str); if (l1 0) then begin s:= pos; if ci then begin repeat (* 1 *) j:= 0; k:= 1; while (s+j (upcase(x[j]) = upcase(p[s+j])) do begin INC (j);// Inc (k); if (j = l1) then begin Result:= s; EXIT; end; end; INC (s, k); until s = f_size; end else begin repeat (* 1 *) j:= 0; k:= 1; while (s+j and (j and (x[j] = p[s+j]) do begin INC (j); Inc (k); if (j = l1) then begin Result:= s; EXIT; end; end; INC (s, k); until s = f_size; end; end; end; end; function TMapTextfile.FindString (const str: string; pos, max: integer): INTEGER; begin Result:= Find (str, Pos, max, False); end; function TMapTextfile.FindStringCI (const str: string; pos, max: INTEGER): integer; begin Result:= Find (str, Pos, max, True); end; function TMapTextfile.FindWildCard (const str: string; pos, max: INTEGER; wild, joker: CHAR): INTEGER; // Like FindString, using wildcards var s, l1, j, k: Integer; p, x: PChar; begin Result:= -1; if f_open then begin if max if pos if pos max then EXIT; x:= PChar (str); p:= PChar (f_view); l1:= 0; while (x[l1] #0) do INC (l1); if (l1 0) then begin s:= pos; repeat (* 1 *) j:= 0; // j = counter in str k:= 0; // k = counter in MMF while (s + k and (j and ((x[j] = p[s + k]) OR (x[j] = joker)) do begin Inc (j); Inc (k); if (x[j] = wild) and (j Inc (j); // search char while (s + k x[j]) do Inc (k); end; if (j = l1) then begin Result:= s; Exit; end; end; INC (s); until s = f_size; end; end; end; function TMapTextfile.GetPtr (pos: Longint): Pchar; // Returns the Position as Pointer (Pchar) to the view begin Result:= CalcPos (pos); end; end.