Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Neuen Beitrag zur Code-Library hinzufügen (https://www.delphipraxis.net/33-neuen-beitrag-zur-code-library-hinzufuegen/)
-   -   Delphi Daten in Datei einfügen bzw. löschen (https://www.delphipraxis.net/150170-daten-datei-einfuegen-bzw-loeschen.html)

flow4.2 9. Apr 2010 01:31


Daten in Datei einfügen bzw. löschen
 
Besonders wenn man große Dateien hat und diese nicht in den Arbeitsspeicher laden kann (will, darf, muss...) ist es nicht ohne weiteres möglich Daten an einer beliebigen Stelle der Datei einzufügen (bzw. zu löschen)...


InsertData fügt Daten an einer beliebigen Position in einen Stream ein
Parameter:
- Stream: Die Datei als TStream
- P: Die Position an der die Daten eingefügt werden sollen
- Len: Die Länge der Daten, die eingefügt werden sollen
- Insert (optional): Die Daten die eingefügt werden sollen (wenn dieser Parameter nicht übergeben wird entsteht einfach ein "Loch" in der Datei - mit irgendwelchen Daten gefüllt)


RemoveData löscht Daten an einer beliebigen Position in einem Stream
Parameter
- Stream: Die Datei als TStream
- P: Die Position an der die Daten gelöscht werden sollen
- Len: Die Länge der Daten, die gelöscht werden sollen
- Removed (optional): Enthält die Daten die gelöscht wurden



Delphi-Quellcode:
procedure InsertData(Stream: TStream; const P, Len: Int64; Insert: TStream = nil);
const
  BufSize = $00010000;
var
  Buf: PChar;
  N: Integer;
  Size, Z: Int64;
begin
  GetMem(Buf, BufSize);
  try
    Z := 0;
    Size := Stream.Size;
    repeat
      if (Size-Z)-P < BufSize then N := (Size-Z)-P else N := BufSize;
      Stream.Position := Size-Z-N;
      Stream.ReadBuffer(Buf^, N);
      Stream.Position := Size-Z-N+Len;
      Stream.WriteBuffer(Buf^, N);
      Inc(Z, N);
    until (Size-Z) = P;
    if Insert <> nil then begin      //Daten von Insert einfügen
      Stream.Position := P;
      Insert.Position := 0;
      if Len <= Insert.Size then Stream.CopyFrom(Insert, Len) else Stream.CopyFrom(Insert, 0);
    end;  
  finally
    FreeMem(Buf, BufSize);
  end;
end;

Delphi-Quellcode:
procedure RemoveData(Stream: TStream; P: Int64; const Len: Int64; Removed: TStream = nil);
const
  BufSize = $00010000;
var
  Buf: PChar;
  N: Integer;
begin
  GetMem(Buf, BufSize);
  try
    if Removed <> nil then begin     //Daten, die gelöscht werden in Removed kopieren
      Stream.Position := P;
      Removed.Position := 0;
      Removed.CopyFrom(Stream, Len);
    end;
    repeat
      if Stream.Size-P-Len < BufSize then N := Stream.Size-P-Len else N := BufSize;
      Stream.Position := P+Len;
      Stream.ReadBuffer(Buf^, N);
      Stream.Position := P;
      Stream.WriteBuffer(Buf^, N);
      Inc(P, N);
    until P = Stream.Size-Len;
    Stream.Size := Stream.Size - Len;
  finally
    FreeMem(Buf, BufSize);
  end;
end;


Dies ist mein erster Post hier überhaupt, also bitte ich schon mal prophylaktisch um Nachsicht... :wink:



jetzt wird TStream statt TFileStream verwendet

Satt Read und Write wird jetzt ReadBuffer und WriteBuffer verwendet
Optionale Paramter Insert und Removed hinzugefügt
BufSize auf $00010000 gesetzt

p80286 9. Apr 2010 10:13

Re: Daten in Datei einfügen bzw. löschen
 
Hallo flow4.2,

könntest Du mir verraten wofür ExtendFile gut sein soll?

Wenn eine Datei eine bestimmte Größe hat, und ich dort Daten einfügen will, dann tu ich das!
Erst die Datei Vergrößern, und dann sie noch einmal anpacken um Daten dort hinein zu schreiben halte ich für nicht sehr effizent. Insbesonders, da ich zum Zeitpunkt des Vergrößerns schon genau wisen muß, wie groß die Daten sind, die ich einfügen will.

Wenn Du schon Platz in einer Datei schaffen willst, solltest Du das aber auch mit definierten Werten tun, damit die Lücke ggf. wieder zu finden ist. Was Du machst, ist ja quasi eine verdoppelung der "Lückendaten".

Gruß
K-H

SirThornberry 9. Apr 2010 10:58

Re: Daten in Datei einfügen bzw. löschen
 
Ich würde anstelle von TFileStream einfach nur TStream wählen. Denn ich sehe keinen Grund warum das nur auf TFileStream begrenzt sein soll. Eventuell baut sich jemand eine eigne TStream-Klasse welche auf Dateien zugreift und diese könnte es aktuell nicht nutzen.

flow4.2 9. Apr 2010 11:14

Re: Daten in Datei einfügen bzw. löschen
 
Hallo p80286!

Naja, versuche doch mal in einen FileStream in der Mitte etwas einzufügen...dann überschreibst du natürlich Daten die dahinter kommen!
Um das zu vermeiden vergrößerst du die Datei mit der Funktion ExtendFile direkt vorher, wobei eine Lücke genau an der Stelle erzeugt wird, an der du Daten einfügen willst.


Beispiel:
Du hast eine Datei (ich nehm jetzt mal ne Textdatei) in der steht: "Dies ist eine Datei..."
Jetzt willst du den String "kleine " an die Position 14 einfügen, damit der Inhalt lautet "Dies ist eine kleine Datei..."
Wenn du jetzt einfach an die Position 14 gehst und den String dort reinschreibst ergibt das ganze "Dies ist eine kleine .", da da " Datei.." von "kleine " überschrieben wird!
Wenn du nun aber ExtendFile verwendest schaffst du praktisch vorher Platz für die Daten die du einfügen willst.

Die Funktion verwendest du praktisch direkt vor dem einfügen --> da ist dir die Länge der einzufügenden Daten ja bekannt

(Natürlich macht das ganze nur bei großen Dateien Sinn)

Gruß

flow4.2 9. Apr 2010 11:45

Re: Daten in Datei einfügen bzw. löschen
 
@SirThornberry
Es wird jetzt TStream verwendet statt TFileStream...

himitsu 9. Apr 2010 12:03

Re: Daten in Datei einfügen bzw. löschen
 
Erstmal das TStream, wie schon erwähnt, damit es vielseitiger verwendbar ist.

PS: man muß einen TStream auch nicht nur in einer TStream-Variable ablegen. :zwinker:

Die Funktion von .Read und .Write bitte prüfen.
http://www.delphipraxis.net/internal...144663#1144663


Und ich würe bei beiden Funktionen noch zusätzlich einen weiteren (optionalen) Parameter einfügen.
Einen weiteren TStream, welcher die Daten enthält/erhält, welche eingefügt oder entfernt werden sollen.

Delphi-Quellcode:
procedure InsertData(Stream: TStream; const P, Len: Int64;
  Insert: TStream = nil); overload;
procedure InsertData(Stream: TStream; const P: Int64; Len: Cardinal;
  Insert: Pointer); overload;

procedure RemoveData(Stream: TStream; const P, Len: Int64;
  Removed: TStream = nil); overload;
procedure RemoveData(Stream: TStream; const P: Int64; Len: Cardinal;
  Removed: Pointer); overload;
So könnte man auch gleichzeitig und über den selben Befehl die neuen Daten in die Lücke einfügen oder die entfernten Daten auslesen.

Als Puffergröße sieht eine $00010000 (runde 64 KB) auch nicht sooo schlecht aus.

PS: Das Ganze über eine Klasse zusammengefaßt oder gar als Class Helper für TStream wäre nicht verkehrt.

flow4.2 9. Apr 2010 14:59

Re: Daten in Datei einfügen bzw. löschen
 
So, hab jetzt mal Read und Write durch ReadBuffer und WriteBuffer ersetzt und die optionalen Parameter hinzugefügt

Ähm ich verstehe nicht ganz warum du die Procedure überlädst...
Ist das nur um einen Pointer übergeben zu können?
Da könnte man doch auch einfach folgendes aufrufen:
Delphi-Quellcode:
InsertData(BeispielStream, BeispielPos, BeispielLen, BeispielPointer as TStream)

flow4.2 9. Apr 2010 15:05

Re: Daten in Datei einfügen bzw. löschen
 
Achja...
Ich habe noch nie mit Class Helper gearbeitet, aber eigentlich müsste das doch so aussehen:
(also es funktioniert so auf jeden Fall :-D )


Delphi-Quellcode:
type
  TStreamHelper = class helper for TStream
  public
    procedure InsertData(const Len: Int64; Insert: TStream = nil);
    procedure RemoveData(const Len: Int64; Removed: TStream = nil);
  end;


...


procedure TStreamHelper.InsertData(const Len: Int64; Insert: TStream = nil);
const
  BufSize = $00010000;
var
  Buf: PChar;
  N: Integer;
  P: Int64;
  SSize, Z: Int64;
begin
  GetMem(Buf, BufSize);
  try
    P := Position;
    Z := 0;
    SSize := Size;
    repeat
      if (SSize-Z)-P < BufSize then N := (SSize-Z)-P else N := BufSize;
      Position := SSize-Z-N;
      ReadBuffer(Buf^, N);
      Position := SSize-Z-N+Len;
      WriteBuffer(Buf^, N);
      Inc(Z, N);
    until (SSize-Z) = P;
    if Insert <> nil then begin      //Daten von Insert einfügen
      Position := P;
      Insert.Position := 0;
      if Len <= Insert.Size then CopyFrom(Insert, Len) else CopyFrom(Insert, 0);
    end;
  finally
    FreeMem(Buf, BufSize);
  end;
end;

procedure TStreamHelper.RemoveData(const Len: Int64; Removed: TStream = nil);
const
  BufSize = $00010000;
var
  Buf: PChar;
  N: Integer;
  P: Int64;
begin
  GetMem(Buf, BufSize);
  try
    P := Position;
    if Removed <> nil then begin     //Daten, die gelöscht werden in Removed kopieren
      Position := P;
      Removed.Position := 0;
      Removed.CopyFrom(Self, Len);
    end;
    repeat
      if Size-P-Len < BufSize then N := Size-P-Len else N := BufSize;
      Position := P+Len;
      ReadBuffer(Buf^, N);
      Position := P;
      WriteBuffer(Buf^, N);
      Inc(P, N);
    until P = Size-Len;
    Size := Size - Len;
  finally
    FreeMem(Buf, BufSize);
  end;
end;

Die Daten werden an der aktuellen Position mit der Länge Len eingefügt bzw. gelöscht


Alle Zeitangaben in WEZ +1. Es ist jetzt 03:33 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz