Einzelnen Beitrag anzeigen

Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
43.132 Beiträge
 
Delphi 12 Athens
 

Re: Textdatei zum Lesen & Schreiben öffnen

  Alt 14. Mär 2007, 17:08
OK, das wäre och noch 'ne Möglichkeit ... hast'e da inzwischen schon was zusammen?

hier mal meine Klasse.
- ist bisher aber noch nicht getestet worden
(nur im Kopf entstanden ... ich hoff mal da sind keine großen Denkfehler drin)
- aber der Compiler meckert schonmal nicht

Overhead wäre hierbei "nur" der aktuell geladene Block,
allerdings würde natürlich beim Speichern eines Blockes eventuell der gesamte nachfolgende Teil verschoben werden.

Delphi-Quellcode:
.
  Type TPartialTextfile = Class
    Private
      _FileName: String;
      _FileHandle: THandle;
      _FileSize, _Start: Int64;
      _OrgLen: Integer;
      _Part: TStringList;
      Function _FindNextLineBreak(Const i: Int64): Integer;
    Public
      Constructor Create;
      Destructor Destroy; Override;
      Property FileName: String Read _FileName;
      Property FileSize: Int64 Read _FileSize;
      Property Start: Int64 Read _Start;
      Property OrgLen: Integer Read _OrgLen;
      Property Part: TStringList Read _Part;
      Function Open (Const FileName: String): Boolean;
      Function LoadPart (Const Pos: Int64; MinLen: Integer): Boolean; Overload;
      Function LoadPart ( {next part} MinLen: Integer): Boolean; Overload;
      Function LoadPartLi(Const Pos: Int64; Lines: Integer): Boolean; Overload;
      Function LoadPartLi( {next part} Lines: Integer): Boolean; Overload;
      Function SavePart: Boolean;
      Procedure Close;
    End;

  Function TPartialTextfile._FindNextLineBreak(Const i: Int64): Integer;
    Var B: Array[0..65535] of Char;
      i2: LARGE_INTEGER;
      W, W2: Cardinal;

    Begin
      Result := 0;
      Repeat
        i2.QuadPart := i + Result;
        i2.LowPart := SetFilePointer(_FileHandle, i2.LowPart, @i2.HighPart, FILE_BEGIN);
        If i2.QuadPart <> i + Result Then Exit;
        ReadFile(_FileHandle, B, SizeOf(B), W, nil);
        If (W <> SizeOf(B)) and (W <> _FileSize - i - Result) Then Exit;
        W2 := 0;
        While W2 < W do Begin
          Inc(W2);
          Case B[W2 - 1] of
            #0, #10: Break;
            #13: Begin
              If W2 > W Then Begin
                i2.QuadPart := i + Result + W2;
                i2.LowPart := SetFilePointer(_FileHandle, i2.LowPart, @i2.HighPart, FILE_BEGIN);
                If i2.QuadPart = i + Result + W2 Then Begin
                  ReadFile(_FileHandle, B, 1, W, nil);
                  If (W = 1) and (B[0] = #10) Then Inc(W2);
                End;
              End Else If B[W2] = #10 Then Inc(W2);
              Break;
            End;
          End;
        End;
        Inc(Result, W2);
      Until W < SizeOf(B);
    End;

  Constructor TPartialTextfile.Create;
    Begin
      _FileHandle := INVALID_HANDLE_VALUE;
      _Part := TStringList.Create;
    End;

  Destructor TPartialTextfile.Destroy;
    Begin
      _Part.Free;
    End;

  Function TPartialTextfile.Open(Const FileName: String): Boolean;
    Var i64: LARGE_INTEGER;

    Begin
      If _FileHandle <> INVALID_HANDLE_VALUE Then Close;
      _FileHandle := CreateFile(PChar(FileName), GENERIC_READ   or GENERIC_WRITE,
        FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, 0);
      If _FileHandle <> INVALID_HANDLE_VALUE Then Begin
        i64.LowPart := GetFileSize(_FileHandle, @i64.HighPart);
        _FileName := FileName;
        _FileSize := i64.QuadPart;
        Result := True;
      End Else Result := False;
    End;

  Function TPartialTextfile.LoadPart(Const Pos: Int64; MinLen: Integer): Boolean;
    Var S: String;
      W: Cardinal;
      i: LARGE_INTEGER;

    Begin
      Result := False;
      If (_FileHandle = INVALID_HANDLE_VALUE) or (Pos > _FileSize) or (MinLen <= 0) Then Exit;
      Try
        SetLength(S, _FindNextLineBreak(Pos + MinLen));
      Except
        Exit;
      End;
      i.QuadPart := Pos;
      i.LowPart := SetFilePointer(_FileHandle, i.LowPart, @i.HighPart, FILE_BEGIN);
      If i.QuadPart <> Pos Then Exit;
      ReadFile(_FileHandle, S[1], Length(S), W, nil);
      If Length(S) <> Integer(W) Then Exit;
      Try
        _Start := Pos;
        _OrgLen := Length(S);
        _Part.Text := S;
      Except
        _OrgLen := 0;
        _Part.Clear;
        Exit;
      End;
      Result := True;
    End;

  Function TPartialTextfile.LoadPart(MinLen: Integer): Boolean;
    Begin
      Result := LoadPartLi(_Start + _OrgLen, MinLen);
    End;

  Function TPartialTextfile.LoadPartLi(Const Pos: Int64; Lines: Integer): Boolean;
    Var S: String;
      W: Cardinal;
      i: LARGE_INTEGER;

    Begin
      Result := False;
      If (_FileHandle = INVALID_HANDLE_VALUE) or (Pos > _FileSize) or (Lines <= 0) Then Exit;
      W := 0;
      While Lines > 0 do Begin
        Inc(W, _FindNextLineBreak(Pos + W));
        If Integer(W) < 0 Then Exit;
        Dec(Lines);
      End;
      Try
        SetLength(S, W);
      Except
        Exit;
      End;
      i.QuadPart := Pos;
      i.LowPart := SetFilePointer(_FileHandle, i.LowPart, @i.HighPart, FILE_BEGIN);
      If i.QuadPart <> Pos Then Exit;
      ReadFile(_FileHandle, S[1], Length(S), W, nil);
      If Length(S) <> Integer(W) Then Exit;
      Try
        _Start := Pos;
        _OrgLen := Length(S);
        _Part.Text := S;
      Except
        _OrgLen := 0;
        _Part.Clear;
        Exit;
      End;
      Result := True;
    End;

  Function TPartialTextfile.LoadPartLi(Lines: Integer): Boolean;
    Begin
      Result := LoadPartLi(_Start + _OrgLen, Lines);
    End;

  Function TPartialTextfile.SavePart: Boolean;
    Var S: String;
      W, W2: Cardinal;
      B: Array[0..65535] of Char;
      i, i2: LARGE_INTEGER;
      i3: Integer;

    Begin
      Result := False;
      If _FileHandle = INVALID_HANDLE_VALUE Then Exit;
      Try
        S := _Part.Text;
      Except
        Exit;
      End;
      i3 := _OrgLen - Length(S);
      If i3 > 0 Then Begin
        i.QuadPart := _Start + _OrgLen;
        While i.QuadPart < _FileSize do Begin
          i2.QuadPart := i.QuadPart;
          i2.LowPart := SetFilePointer(_FileHandle, i2.LowPart, @i2.HighPart, FILE_BEGIN);
          If i2.QuadPart <> i.QuadPart Then Exit;
          ReadFile(_FileHandle, B, SizeOf(B), W, nil);
          If (W <> SizeOf(B)) and (W <> _FileSize - i.QuadPart) Then Exit;
          W2 := W;
          i2.QuadPart := i.QuadPart - i3;
          i2.LowPart := SetFilePointer(_FileHandle, i2.LowPart, @i2.HighPart, FILE_BEGIN);
          If i2.QuadPart <> i.QuadPart - i3 Then Exit;
          WriteFile(_FileHandle, B, W2, W, nil);
          If W <> W2 Then Exit;
          Inc(i.QuadPart, SizeOf(B));
        End;
      End Else If i3 < 0 Then Begin
        i3 := -i3;
        i.QuadPart := _Start + _OrgLen;
        Inc(i.QuadPart, (_FileSize - i.QuadPart) and -SizeOf(B));
        While i.QuadPart >= _Start + _OrgLen do Begin
          i2.QuadPart := i.QuadPart;
          i2.LowPart := SetFilePointer(_FileHandle, i2.LowPart, @i2.HighPart, FILE_BEGIN);
          If i2.QuadPart <> i.QuadPart Then Exit;
          ReadFile(_FileHandle, B, SizeOf(B), W, nil);
          If (W <> SizeOf(B)) and (W <> _FileSize - i.QuadPart) Then Exit;
          W2 := W;
          i2.QuadPart := i.QuadPart + i3;
          i2.LowPart := SetFilePointer(_FileHandle, i2.LowPart, @i2.HighPart, FILE_BEGIN);
          If i2.QuadPart <> i.QuadPart + i3 Then Exit;
          WriteFile(_FileHandle, B, W2, W, nil);
          If W <> W2 Then Exit;
          dec(i.QuadPart, SizeOf(B));
        End;
      End;
      Dec(_FileSize, i3);
      i.QuadPart := _FileSize;
      i.LowPart := SetFilePointer(_FileHandle, i.LowPart, @i.HighPart, FILE_BEGIN);
      If i.QuadPart = _FileSize Then SetEndOfFile(_FileHandle);
      i.QuadPart := _Start;
      i.LowPart := SetFilePointer(_FileHandle, i.LowPart, @i.HighPart, FILE_BEGIN);
      If i.QuadPart <> _Start Then Exit;
      WriteFile(_FileHandle, S[1], Length(S), W, nil);
      _OrgLen := Length(S);
      Result := Length(S) <> Integer(W);
    End;

  Procedure TPartialTextfile.Close;
    Begin
      CloseHandle(_FileHandle);
      _FileName := '';
      _FileHandle := INVALID_HANDLE_VALUE;
      _FileSize := 0;
      _Start := 0;
      _OrgLen := 0;
      _Part.Clear;
    End;
Delphi-Quellcode:
// Edit: TEdit;
// Button1: TButton;

  Procedure TForm1.Button1Click(Sender: TObject);
    Var PT: TPartialTextfile;

    Begin
      PT := TPartialTextfile.Create;
      PT.Open('Unit1.pas');
      Memo1.Lines.Clear;
      Repeat
        PT.LoadPartLi(10);
        Memo1.Lines.Add(Format('*** %d, %d/%d', [PT.Start, PT.OrgLen, Length(PT.Part.Text)]));
        Memo1.lines.AddStrings(PT.Part);
        Memo1.Lines.Add('');
      Until PT.Part.Text = '';
      PT.Free;
    End;
[edit]
Fehler im Code behoben
das Auslesen funktioniert anscheinend
und auch eine kleine Testprozedur ist nun mit dabei


[add]
wenn du die geänderten Strings erstmal hinter dem "originalem" Dateiende in der Datei speicherst, dann würde auch einer dieser beiden Varianten für deine Idee ausreichen ... außerdem würden die dann nicht im RAM liegen, was bei vielen Änderungen in großen Dateien besser wäre.
Delphi-Quellcode:
PLine = ^TLine;
TLine = packed record
  Next, Prev: PLine;
  TextPos: Int64;
  TextLen: Integer;
end;

TLineArray = Array of Record
  TextPos: Int64;
  TextLen: Integer;
end;
Garbage Collector ... Delphianer erzeugen keinen Müll, also brauchen sie auch keinen Müllsucher.
my Delphi wish list : BugReports/FeatureRequests
  Mit Zitat antworten Zitat