Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi im FileStream bestimmte Zeichen ersetzen (https://www.delphipraxis.net/135032-im-filestream-bestimmte-zeichen-ersetzen.html)

fips0705 3. Jun 2009 14:41


im FileStream bestimmte Zeichen ersetzen
 
ich habe mit
Delphi-Quellcode:
TFileStream.Create(opendialog1.filename, fmOpenReadWrite or fmShareDenyWrite)
eine Datei gelesen und möchte nur im Stream bestimme Zeichen wie z.B. Hex 00 durch Leerzeichen ersetzen

mit der Funktion StringReplace funktioniert das im Filestream nicht

jaenicke 3. Jun 2009 14:47

Re: im FileStream bestimmte Zeichen ersetzen
 
Handelt es sich um eine Textdatei? Dann würde ich keinen TFileStream benutzen.

Was du bei einem FileStream machen kannst wäre z.B. byteweise ein Byte lesen, vergleichen und ggf. die Position ein Byte zurücksetzen und das Ersatzbyte schreiben. Das wäre aber sehr langsam.

Was sinnvoll ist, hängt vom Dateityp und der Dateigröße ab. Möglich wäre z.B. den Inhalt in einen String oder Bytearray zu kopieren, usw.

fips0705 3. Jun 2009 14:53

Re: im FileStream bestimmte Zeichen ersetzen
 
Es ist im Prinzip eine Textdatei, Sie enthält aber Drucksteuerzeichen. Das bewirkt, das es mit einer Stringlist oder auch Memoliste nicht funktioniert. Hier wir immer nur ein Teil gelesen.

Habe es auch schon mit BlockRead versucht.

Leider weiß ich nicht wie ich die komplette Datei einlese und dann von Anfang bis Ende die Datei durchsuche.

Bernhard Geyer 3. Jun 2009 14:59

Re: im FileStream bestimmte Zeichen ersetzen
 
Zitat:

Zitat von fips0705
Es ist im Prinzip eine Textdatei, Sie enthält aber Drucksteuerzeichen.

Bist du dir sicher das es nicht eine Unicode-Textdatei ist? Was sagt notepad dazu wenn du es damit öffnest?

fips0705 3. Jun 2009 15:05

Re: im FileStream bestimmte Zeichen ersetzen
 
M F Bitte bei der Zahlung angeben

so sieht die Datei mit notepad aus.
lässt sich aber komplet öffnen

shmia 3. Jun 2009 15:05

Re: im FileStream bestimmte Zeichen ersetzen
 
Mit den beiden folgende Funktionen kannst du die komplette Datei als String einlesen, verändern und wieder speichern (am Besten unter einem anderen Dateinamen).
Wenn deine Datei ~ 10 Megabytes überschreitet musst du dir Gedanken machen, nur Teile der Datei einzulesen und zu manipulieren.
So aber ist es am Einfachsten: alles in den Speicher lesen, verändern, zurückschreiben.

Delphi-Quellcode:
// kopiert aus der JCL
function FileToString(const FileName: string): AnsiString;
var
  fs: TFileStream;
  Len: Integer;
begin
  fs := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
  try
    Len := fs.Size;
    SetLength(Result, Len);
    if Len > 0 then
      fs.ReadBuffer(Result[1], Len);
  finally
    fs.Free;
  end;
end;

procedure StringToFile(const FileName: string; const Contents: AnsiString);
var
  fs: TFileStream;
  Len: Integer;
begin
  fs := TFileStream.Create(FileName, fmCreate);
  try
    Len := Length(Contents);
    if Len > 0 then
      fs.WriteBuffer(Contents[1], Len);
  finally
    fs.Free;
  end;
end;

himitsu 3. Jun 2009 15:32

Re: im FileStream bestimmte Zeichen ersetzen
 
du kannst die komplette Datei vom Stream kurz in einen Puffer laden, darin die Zeichen ersetzen und dann zurück in en Stream, oder eben Stückchenweise (Byteweise oder in größeren Stückchen)

man könnte auch auch 2 Streams nutzen ... mit einem Stream aus der Datei lesen, dann da stückchenweise raus, bearbeiten und in den anderen stream rein

und statt die Datei danach wieder zurück in den Stream zu schicken, könnte man sie auch gleich im Puffer (z.B. einem String) belassen (siehe shmia)

fips0705 4. Jun 2009 06:41

Re: im FileStream bestimmte Zeichen ersetzen
 
vielen Dank für die vielen Tipps.
nur weiß ich nicht wie ich in einem Strem bzw. Puffer Zeichen ersetzte.
welchen Befehl kann ich dafür benutzen.

Satty67 4. Jun 2009 06:52

Re: im FileStream bestimmte Zeichen ersetzen
 
Stell Dir den Stream als großes Byte-Array vor. Du kannst nur nicht direkt darauf zugreifen, sondern musst einzelne Byte (oder Byteblöcke) mit Stream.Methoden lesen/schreiben
Delphi-Quellcode:
Stream.Read(aByte, SizeOf(aByte))
Stream.Write(aByte, SizeOf(aByte))
Also im Prinzip nach dem öffnen zweier Streams aus dem einen ein Byte lesen... prüfen und evtl. anderen Wert zuweisen... dann Byte in zweiten Stream schreiben.

fips0705 4. Jun 2009 10:13

Re: im FileStream bestimmte Zeichen ersetzen
 
Ich habe es jetzt so gelöst vielen Dank

Delphi-Quellcode:
function FileToStringErsetze(const FileName: string;NeueZeichen: string;zuErsetzendeZeichen:integer): AnsiString;
var
  fs: TFileStream;
  Len,j: Integer;
  temp: string;

begin
  fs := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
  try
    Len := fs.Size;
    SetLength(Result, Len);
    if Len > 0 then
      fs.ReadBuffer(Result[1], Len);
      for j := 1 to Len do
        if Ord(Result[j]) = zuErsetzendeZeichen then
          Temp := Temp + NeueZeichen
        else
          Temp := Temp + Result[j];

  finally
    Result:=Temp;
    fs.Free;
  end;
end;

Aufruf über
Delphi-Quellcode:
  SL := TStringList.create ;
  SL.add(FileToStringErsetze(opendialog1.filename,'|',00));

himitsu 4. Jun 2009 10:57

Re: im FileStream bestimmte Zeichen ersetzen
 
nja, jedes Zeichen einzeln an den Temp-String anzuhängen ist nicht grad optimal
(da wird z.B. jedesmal der komplette alte Temp-String, zusammen mit dem neuen Zeichen, in einen neuen Temp-String umkopiert)

du kannst die Daten auch direkt im String ersetzen :-D

Delphi-Quellcode:
function FileToStringErsetze(const FileName: String; zuErsetzendeZeichen, NeueZeichen: AnsiChar): AnsiString;
var fs: TFileStream;
begin
  fs := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
  try
    SetLength(Result, fs.Size);
    if fs.Size > 0 then fs.ReadBuffer(Result[1], Length(Result));
    for i := 1 to Length(Result) do
      if Result[i] = zuErsetzendeZeichen then Result[i] := NeueZeichen;
  finally
    fs.Free;
  end;
end;

s := FileToStringErsetze(opendialog1.filename, #00, '|');


falls dich noch andere Zeichen stören, so könnte man z.B. gleich alle Steuerzeichen (unterhalb des Leertaste) ersetzen ... abgesehn von Zeilenumbruch und Tabulator
Delphi-Quellcode:
function FileToStringErsetze(const FileName: String; NeueZeichen: AnsiChar): AnsiString;
var fs: TFileStream;
begin
  fs := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
  try
    SetLength(Result, fs.Size);
    if fs.Size > 0 then
      fs.ReadBuffer(Result[1], Length(Result));
    for i := 1 to Length(Result) do
      if Result[i] in [#0..#8, #11, #12, #14..#31] then
        Result[i] := NeueZeichen;
  finally
    fs.Free;
  end;
end;

s := FileToStringErsetze(opendialog1.filename, '|');

Satty67 4. Jun 2009 11:21

Re: im FileStream bestimmte Zeichen ersetzen
 
Hab es mal gemessen... die Variante von himitsu ist etwa 50x schneller :stupid:

Falls es überhaupt noch jemand interessiert ;)
Delphi-Quellcode:
procedure MakeZeroFilledFile;
var
  FileStream : TFileStream;
  Buffer : array[0..1023] of Byte;
  i : Integer;
begin
  FileStream := TFileStream.Create('F:\WorkTemp\Leerfile.dat', fmCreate);
  FillChar(Buffer,SizeOf(Buffer),#0);
  for i := 0 to (1024*10) do
    FileStream.Write(Buffer,SizeOf(Buffer));
  FileStream.Free;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  aString : String;
begin
  MakeZeroFilledFile;
  StopUhr.Start;
  aString := FileToStringErsetze2('F:\WorkTemp\Leerfile.dat','X',#0);
  StopUhr.Stop;
  ShowMessage(IntToStr(Length(aString))+' - '+StopUhr.StoppedTimeStr);

  MakeZeroFilledFile;
  StopUhr.Start;
  aString := FileToStringErsetze('F:\WorkTemp\Leerfile.dat','X',0);
  StopUhr.Stop;
  ShowMessage(IntToStr(Length(aString))+' - '+StopUhr.StoppedTimeStr);
end;

p80286 4. Jun 2009 11:26

Re: im FileStream bestimmte Zeichen ersetzen
 
Hallo zusammen,

falls es sich um das Ersetzen von einzelnen Zeichen dreht, nutze ich immer so etwas:

Delphi-Quellcode:
var
  UT = array [0..255] of byte;

...
  // array initialisieren
  for i:=0 to 255 do
    UT[i]:=i;
  //zu übersetzende Zeichen definieren
  UT[$0A]:=$20;
....

for i:=0 to sizeof(buffer)-1 do
  buffer[i]:=UT[buffer[i]];
Gruß
K-H

[edit] Die Zeit hab ich noch nie gemessen! [/edit]

fips0705 4. Jun 2009 13:05

Re: im FileStream bestimmte Zeichen ersetzen
 
Vielen Dank, dann werde ich wohl den Code von himitsu nehmen da er auch profesioneller ist und wesentlich schnellere.

shmia 4. Jun 2009 17:04

Re: im FileStream bestimmte Zeichen ersetzen
 
Nun, dann sind wir doch wieder bei meinen Funktionen von Betrag #6.
Es ist doch programmiertechnisch besser die Aufgabe wie folgt zu lösen:
Delphi-Quellcode:
var
  daten : string;
begin
  daten := FileToString(dateiname);
  StrReplaceCharsInplace(daten, [#0..#31], '|'); // Steuerzeichen durch Pipezeichen ersetzen
  StringToFile(daten, dateiname);
end;

function StrReplaceCharsInplace(var S: AnsiString; const Chars: TSysCharSet; Replace: AnsiChar): AnsiString;
var
  I: Integer;
begin
  for I := 1 to Length(S) do
    if S[I] in Chars then
      S[I] := Replace;
end;
Die verwendeten Funktionen FileToString, ReplaceCharInplace und StringToFile haben jeweils eine ganz begrenzte Aufgabe.
Man könnte die Funktionen als "atomar" bezeichnen - man kann nichts weglassen ohne dass die gesamte Funktion nutzlos würde.
Diese Funktionen sollten in keiner Bibliothek fehlen; man kann sie immer wieder verwenden!

Die Funktion FileToStringErsetze() ist dagegen ziemlich speziell und wäre in einer Bibliothek eher weniger nützlich.

p80286 5. Jun 2009 14:06

Re: im FileStream bestimmte Zeichen ersetzen
 
Hallo zusammen,

Ich habe aus lauter Langeweile einmal ausprobiert ob StrReplaceCharsInplace oder meine "ArrayLösung" schneller ist. Selbst unter Beachtung von "wer misst misst Mist" bin ich auf folgendes Ergebnis gestoßen:

Bis ca. 5-10 MB Dateigröße hat StrReplaceCharsInplace die Nase vorn, erst ab ca. 50 MB ist die "ArrayLösung" schneller.

Abgesehen davon, daß selten so große oder größere Dateien verarbeitet werden, und die Geschwindigkeit immer noch "gefühlt" gleich ist, irritiert mich, daß der Initialisierungsoverhead sich so stark bemerkbar macht.

Habt Ihr eine Erklährung für mich?

Gruß
K-H

shmia 5. Jun 2009 14:21

Re: im FileStream bestimmte Zeichen ersetzen
 
Die "Array-Lösung" ist vorallem dann angebracht, wenn man beliebige Umschlüsselungen von einzelnen Zeichen vornehmen möchte.
Falls man nur ein einziges Ersetzungzeichen hat (im deinem Beispiel das '|') dann wird wohl folgende Funktion
die maximale Leistung bringen:
Delphi-Quellcode:
function StrReplaceCharsInplace(var S: AnsiString; const Chars: TSysCharSet; Replace: AnsiChar): AnsiString;
var
  I: Integer;
  p : PChar;
begin
  UniqueString(S);
  p := PChar(S);
  for I := Length(S)-1 downto 0 do
  begin
    if p^ in Chars then
      p^ := Replace;
    Inc(p);
  end;
end;

p80286 5. Jun 2009 14:37

Re: im FileStream bestimmte Zeichen ersetzen
 
Hallo Andreas,

Daß diese Lösung schnell ist (für mich überraschend, da zu Pascal-Zeiten Sets die Bremse überhaupt waren) kann ich nur bestätigen.

Aber gefühlt kann ich nicht glauben das
Delphi-Quellcode:
If x in IrgendeinerMenge then
schneller sein soll als ein simples
Delphi-Quellcode:
for i:=1 to z do
Das es so ist habe ich ja gemerkt, aber warum?

Gruß
K-H

P.S.
Bringt der Zugriff über Pointer und mir Rückwärtszählung Soo viel?

shmia 5. Jun 2009 14:53

Re: im FileStream bestimmte Zeichen ersetzen
 
Zitat:

Zitat von p80286
Delphi-Quellcode:
If x in IrgendeinerMenge then

In Pseudocode sieht das ungefähr so aus:
Delphi-Quellcode:
if (@IrgendeinerMenge + (x shr 3 ))^ and (1 shl (x and $7)) <> 0 then
Man testet praktisch, ob das x.te Bit in der Menge gesetzt ist.
Die Operationen Addition, Links/Rechtsschieben, AND-Verknüpfung sowie Vergleich mit 0 sind alle recht flott.
Zitat:

Zitat von p80286
Bringt der Zugriff über Pointer und mir Rückwärtszählung Soo viel?

Der Pointerzugriff macht schon Einiges aus; die Rückwärtsschleife dagegen so gut wie nichts,
weil der Compiler sowieso eine Rückwärtsschleife angesetzt hätte, weil die Indexvariable nicht benützt wird.

p80286 5. Jun 2009 15:26

Re: im FileStream bestimmte Zeichen ersetzen
 
Danke!

Gut zu wissen warum!

Gruß
K-H


Alle Zeitangaben in WEZ +1. Es ist jetzt 04:24 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