![]() |
Zeilenumbruch ersetzen Algorithmus
Hallo liebe Community,
ich lese teils sehr große Textdateien häppchenweise via Chunks mittels TFileStream ein. Später parse ich Zeile für Zeile. Nun gibt/gab es ja verschiedene Betriebssysteme, die unterschiedlich mit Zeilenumbrüchen umgehen. Ich gebe Zeilenumbrüche in Dateien nur noch mit dem Hex-Wert „0A“ aus. Es gibt aber auch die Kombination „0D0A“ und „0D“. Ich bin jetzt kein Programmierprofi. Wie kann man mittels TFileStream nach den Kombinationen „0D0A“ (2 Bytes) und „0D“ (1 Byte) suchen und diese durch „0A“ ersetzen? Ich denke das ganze über TFileStream zu lösen ist wesentlich schneller und ressourcenschonender, als String Replace Methoden zu verwenden. Grade bei einer großen Textdatei von knapp 700 Megabyte. Ich hoffe, ich habe meine Frage nachvollziehbar formuliert. Viele Grüße |
AW: Zeilenumbruch ersetzen Algorithmus
Schau dir mal in Sysutils die Function
Delphi-Quellcode:
an. Die arbeitet zwar auf
AdjustLineBreaks
Delphi-Quellcode:
, aber intern kommen auch nur
string
Delphi-Quellcode:
zum Einsatz. Das Verfahren lässt sich relativ leicht auf
PChar
Delphi-Quellcode:
umbauen. Du musst halt zusätzlich auf das Encoding achten.
Streams
|
AW: Zeilenumbruch ersetzen Algorithmus
Zitat:
Es geht kein Weg daran vorbei, die zu verarbeitenden Daten in den Speicher zu laden. Die Frage ist "nur" wie groß die Happen sind die verarbeitet werden. Gruß k-H |
AW: Zeilenumbruch ersetzen Algorithmus
Ich weiß ja nicht wie Deine Ausgabe der Text-Datei vonstatten geht, erstellt dein Parser einen String pro Zeile?
Falls ja, check doch lediglich die letzten 2 zeichen nach deinen Wert und ersetz das dann. Nachtrag: Bzw. implementier in deinem Parser diese Zusatzfunktion, da du ja bereits eine Zeile in irgendeiner art und weise besitzt check die letzten 2 bytes. Ein Einblick in deinen Parser wäre hilfreich. Weiterer Nachtrag:
Delphi-Quellcode:
//hier ein mini Beispiel für Strings, mit bissl justierung auch für PChar's bzw alles wo man "position + wert" nutzt, daß Prinzip bleibt immer das selbe
Function CalibrateString( EineParserZeile : String ) : String; begin // letzte 2 zeichen nach 0D0A checken if Length(EineParserZeile) > 1 then // falls man einen minimum längen-check einbaut wird's sicherer. optional. if ((EineParserZeile[Length(EineParserZeile)-1] = char($0D)) and (EineParserZeile[Length(EineParserZeile)] = char($0A))) then begin EineParserZeile[Length(EineParserZeile)-1] := char($0A); // hier fehlt ein aufruf um das letzte zeichen zu entfernen, ich hab grad kein delphi parat aber für Strings würd ich einen Copy() Befehl verwenden. end; // letztes zeichen nach 0D checken if Length(EineParserZeile) >= 1 then // für mehr Speed kann man auch die ausführung oben mit einbauen aber falls tatsächlich mal eine Zeile mit nur einem char($0D) reinkommt wird die nicht verarbeitet if EineParserZeile[Length(EineParserZeile)] = char($0D) then EineParserZeile[Length(EineParserZeile)] := char($0A); // Rückgabe an dein Programm Result := EineParserZeile; end; //ps: als "Case" kann man das ganze auch verpacken. |
AW: Zeilenumbruch ersetzen Algorithmus
grober Vorschlag:
zwei Streams nehmen: 1. Stream = Input 2. Stream = Output 1. Stream byteweise (oder charweise) lesen. 2. jedes Zeichen abfragen, ob es in den Output soll. 3. Zeichen schreiben oder verwerfen. ungefähr (ungetestet) sowas:
Delphi-Quellcode:
Das sollte auch bei größeren Datenmengen noch vertretbar schnell sein.
procedure TIrgendeineKlasse.Zeichenaustauschen(input : TMemoryStream; output : TMemoryStream);
Var ch : Char; begin input.Position := 0; output.Position := 0; // Ist überhaupt was in der Datei drin? if input.Read(ch, 1) > 0 then begin repeat case ch of // Hier zeichenweise entscheiden, was geschehen soll. #10 : begin output.Write(ch,1); end; #13 : begin // output.Write(ch,1); end; else // Übrige Zeichen ausgeben. output.Write(ch,1); end; until input.Read(ch, 1) = 0; input.Position := 0; output.Position := 0; end; end; |
AW: Zeilenumbruch ersetzen Algoritmud
Vielen Dank schon einmal für die vielen Beitröge. Ich versuche darauf einzugehen. Wie gesagt, ich bin kein Profi und mir geht es eher um einen Aufbau des Algorithmus.
Zitat:
Zitat:
Zitat:
|
AW: Zeilenumbruch ersetzen Algorithmus
Im Case kannst Du jeweils das zuletzt gelesene Zeichen abfragen.
Findest Du nun eine #13 und willst sie nicht ausgeben, so wird sie halt nicht geschrieben. Oder anders ausgedrückt: In der Else werden alle die Zeichen ausgegeben, die nicht vorher im Case irgendwie verarbeitet wurden. Soll also z. B. die #13 nie ausgegeben werden, sähe der Quelltext so aus:
Delphi-Quellcode:
Oder die Antwort auf deine Frage Wenn ich jetzt auf #13 treffe, was muss ich dann machen? ist: Nichts.
procedure TIrgendeineKlasse.Zeichenaustauschen(input : TMemoryStream; output : TMemoryStream);
Var ch : Char; begin input.Position := 0; output.Position := 0; // Ist überhaupt was in der Datei drin? if input.Read(ch, 1) > 0 then begin repeat case ch of #13 : ; // Das Zeichen wird schlicht und einfach ignoriert. else output.Write(ch,1); // Übrige Zeichen ausgeben. end; until input.Read(ch, 1) = 0; input.Position := 0; output.Position := 0; end; end; Alle anderen Zeichen werden ausgegeben. |
AW: Zeilenumbruch ersetzen Algorithmus
Zitat:
Zitat:
|
AW: Zeilenumbruch ersetzen Algorithmus
Zitat:
Delphi-Quellcode:
procedure TIrgendeineKlasse.Zeichenaustauschen(input : TMemoryStream; output : TMemoryStream);
Var ch1 : Char; ch2 : Char; begin input.Position := 0; output.Position := 0; // Ist überhaupt was in der Datei drin? if input.Read(ch1, 1) > 0 then begin repeat case ch of #13 : begin // Das nächste Zeichen lesen. if input.Read(ch2, 1) <> 0 then begin case ch2 of #10 : ; // ignorieren, wird als nächstes Zeichen gelesen. else begin // wenn wir 'ne #13 erhalten und keine #10 folgt, geben wir 'ne #10 aus. ch2 := #10; output.Write(ch2,1); end; end; // und wieder ein Zeichen zurück. input.Position := input.Position - 1; end; end; else output.Write(ch1,1); // Übrige Zeichen ausgeben. end; until input.Read(ch1, 1) = 0; input.Position := 0; output.Position := 0; end; end; |
AW: Zeilenumbruch ersetzen Algorithmus
Zitat:
1. Auf der einen Seite werden ch1 und ch2 als Char (2 Bytes) deklariert, mit Read wird aber nur jeweils 1 Byte eingelesen. Damit ist das höhere Byte undefiniert und das case könnte daneben gehen. Da wir ja hier alle wohl von eine ANSI- bzw. UTF8-codierten Textdatei ausgehen, würde ich direkt mit Byte (oder AnsiChar) arbeiten. 2. Der Code unterschlägt das letzte Zeilenende, wenn dieses durch ein einzelnes #13 gekennzeichnet ist. 3. Eine Unicode-Datei könnte man damit nicht direkt umsetzen, da der komplette Bereich 0D00-0DFF sowie alle Zeichen mit 0D im Low-Byte falsch behandelt werden. Ich vermute aber, daß dies hier nicht relevant sein wird (siehe 1). |
Alle Zeitangaben in WEZ +1. Es ist jetzt 06:07 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 by Thomas Breitkreuz