Autor |
Beitrag |
UweK
      
Beiträge: 52
Erhaltene Danke: 1
Win 7
Delphi Enterprise XE6
|
Verfasst: Fr 29.06.12 15:06
Guten Tag,
Ich möchte in einem ASCII-File mit der Zeilenstruktur "<Keywort> = <Wert>" bei einem <Wert> binäre Daten unterbringen, die ich natürlich MIME codiere, damit der File problemlos in einem Editor bearbeitbar bleibt.
Die binären Daten sind groß (über 100 kB), und nach MIME noch ein ganzes Stück größer. Deshalb würde ich die Daten vor dem MIME gern komprimieren. ZIP wäre dafür z.B. gut geeignet. Ich habe die Daten mal testweise in eine Extradatei geschrieben und diese dann mit einem normalen ZIP-Programm komprimiert. Durch viele "symmetrische Muster" in meinen Daten schrumpfte das Ergebnis dann auf 1 kB Größe. Es könnte aber sicherlich auch irgendein anderer Kompressor als ZIP sein, denn ich möchte den <Wert> nur innerhalb meines Programmes in die ASCII-Files schreiben und wieder zurück lesen. Es sollte aber möglichst keine extra DLL sein, damit ich mein Programm bequem weitergeben kann.
Im Idealfall stelle ich mir etwas ganz Simples in dieser Form vor:
Delphi-Quelltext 1: 2:
| ZippedString:= ZIP(DatenString); ErgebnisString:= MIME(ZippedString); | Ich habe mir bereits diverse Webseiten mit ZIP Tools angeschaut (ZipForge, Abbrevia, TZipMaster usw.), bin bei deren Beschreibungen aber in der Fülle von Funktionen ertrunken, die ich alle nicht brauche, ohne dabei einen Hinweis auf meinen gewünschen Mini-Zweck zu finden.
Ich würde mich sehr freuen, wenn mir jemand von Euch einen Tipp aus der eigenen Erfahrung geben könnte.
Moderiert von Narses: Delphi-Tags hinzugefügt
|
|
Martok
      
Beiträge: 3661
Erhaltene Danke: 604
Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
|
Verfasst: Fr 29.06.12 15:22
Da du eigentlich ja nicht das Datenformat brauchst, dürfte T(De)CompressionStream (Unit zlib) das sein, was du suchst.
Ungefähr so (ungetestet, im Beitragseditor getippt):
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
| uses zlib; function Zip(S:String): string; var src,dst: TStringStream; comp: TCompressionStream; begin src:= TStringStream.Create(S); dst:= TStringStream.Create(''); comp:= TCompressionStream.Create(dst); try comp.CopyFrom(src, -1); Result:= dst.DataString; finally end end; |
_________________ "The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
Für diesen Beitrag haben gedankt: UweK
|
|
Narses
      

Beiträge: 10183
Erhaltene Danke: 1256
W10ent
TP3 .. D7pro .. D10.2CE
|
Verfasst: Fr 29.06.12 15:44
Moin!
UweK hat folgendes geschrieben : | die ich natürlich MIME codiere |
Ich nehme mal an, du meinst base64, weil MIME ist keine Codierung, sondern ein Protokoll.
Hier mein Vorschlag (getestet  ):
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34:
| uses ..., zlib, base64;
procedure TForm1.btnEncodeClick(Sender: TObject); var Data: AnsiString; CompressedData: TStringStream; Compressor: TCompressionStream; begin Data := Memo1.Text; CompressedData := TStringStream.Create(''); Compressor := TCompressionStream.Create(clMax, CompressedData); Compressor.Write(PAnsiChar(Data)^, Length(Data)); Compressor.Free; Memo2.Text := B64Encode(CompressedData.DataString); CompressedData.Free; end;
procedure TForm1.btnDecodeClick(Sender: TObject); var CompressedData: TStringStream; Decompressor: TDecompressionStream; C: AnsiChar; Data: AnsiString; begin CompressedData := TStringStream.Create(B64Decode(Trim(Memo2.Text))); Decompressor := TDecompressionStream.Create(CompressedData); Data := ''; while Decompressor.Read(C, 1) = 1 do Data := Data +C; Memo1.Text := Data; Decompressor.Free; CompressedData.Free; end; | Ich gestehe, ich habe das jetzt nicht auf Unicode-String-tauglichkeit hin gemacht, sondern einfach so, wie mein D7 es erlaubt. Also, bei Unicode-Delphi-Versionen Vorsicht mit dem TStringStream, da müsste man dann den Umweg über einen TMemoryStream gehen oder so. Laut jaenicke geht das sogar unter Unicode-Delphi-Versionen.
cu
Narses
_________________ There are 10 types of people - those who understand binary and those who don´t.
Zuletzt bearbeitet von Narses am Sa 30.06.12 10:02, insgesamt 1-mal bearbeitet
|
|
Martok
      
Beiträge: 3661
Erhaltene Danke: 604
Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
|
Verfasst: Fr 29.06.12 17:38
_________________ "The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
|
|
jaenicke
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Fr 29.06.12 19:53
Der Quelltext von Narses funktioniert auch unter XE2 problemlos. 
Für diesen Beitrag haben gedankt: Martok
|
|
Narses
      

Beiträge: 10183
Erhaltene Danke: 1256
W10ent
TP3 .. D7pro .. D10.2CE
|
Verfasst: Fr 29.06.12 21:23
_________________ There are 10 types of people - those who understand binary and those who don´t.
|
|
jaenicke
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Sa 30.06.12 04:55
Für diesen Beitrag haben gedankt: Blawen, Narses
|
|
UweK 
      
Beiträge: 52
Erhaltene Danke: 1
Win 7
Delphi Enterprise XE6
|
Verfasst: Mi 11.07.12 15:33
Hallo,
Vielen Dank für eure Tipps. Ich kam erst jetzt dazu, mich damit zu beschäftigen.
Zunächst einmal funktionierte bei mir leider gar nichts davon, bis ich feststellte: Das Zippen geht tatsächlich fehlerfrei und so wie von euch beschrieben. Aber das Problem liegt bei mir noch beim base64. Dieses habe ich nicht in meinem Delphi 2010, und benutze deshalb ersatzweise IdEncoderMime/IdDecoderMime von Indy in dieser Form (meine Änderungen an eurem obigen Quelltext habe ich gekennzeichnet):
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39:
| uses zlib, IdCoderMime; procedure TTestForm.btnEncodeClick(Sender: TObject); var Data: AnsiString; CompressedData: TStringStream; Compressor: TCompressionStream; IdEncoderMIME: TIdEncoderMIME; begin IdEncoderMime:= TIdEncoderMIME.Create; Data:= Memo1.Text; CompressedData := TStringStream.Create(''); Compressor := TCompressionStream.Create(clMax, CompressedData); Compressor.Write(PAnsiChar(Data)^, Length(Data)); Compressor.Free; Memo2.Text := IDEncoderMIME.EncodeString(CompressedData.DataString); CompressedData.Free; IdEncoderMIME.Free; end;
procedure TTestForm.btnDecodeClick(Sender: TObject); var CompressedData: TStringStream; Decompressor: TDecompressionStream; C: AnsiChar; Data: AnsiString; IdDecoderMIME: TIdDecoderMIME; begin IdDecoderMime:= TIdDecoderMIME.Create; CompressedData := TStringStream.Create(IdDecoderMime.DecodeString(Trim(Memo2.Text))); Decompressor := TDecompressionStream.Create(CompressedData); Data := ''; while Decompressor.Read(C, 1) = 1 do Data := Data +C; Memo3.Text := Data; Decompressor.Free; CompressedData.Free; IdDecoderMIME.Free; end; |
Das produziert bei bestimmten in Memo1 eingetippten Strings Fehlerabbrüche in Zeile (***) schon beim Decodieren des ersten Zeichens, während sehr ähnliche Strings korrekt bearbeitet werden. Zum Beispiel (jeweils ohne die " eingetippt):
OK: "Normaler Text!!!" (mit 3x Ausrufezeichen), "!!!" (3x Ausrufezeichen), "Text!!!!" (mit 4x Ausrufezeichen)
Fehler: "Normaler Text!!!!" (mit 4x Ausrufezeichen), "!!!!" (4x Ausrufezeichen), "Text!!!" (mit 3x Ausrufezeichen)
Das sieht alles irgendwie unsystematisch aus, wie ein Bug in Indy. Oder benutze ich Indy da falsch?
Der Effekt speziell mit den mehrfachen Ausrufezeichen fiel mir auf, weil meine Strings immer solche Zeichenfolgen enthalten. Ein typischer String wäre:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8:
| MeinString:= ''; for I:= 0 to 31 do MeinString:= MeinString + Chr(255); for I:= 0 to 111 do for J:= 0 to 7 do MeinString:= MeinString + Chr(I); for I:= 928 to 1023 do MeinString:= MeinString + Chr(255); |
Meine zu verarbeitenden Strings sind immer genau 1024 Zeiichen lang, und immer von dieser Struktur mit aufsteigendem Chr(I), vorn und hinten mit Chr(255), nur gelegentliche Variationen in der Anzahl jedes Zeichens, also z.B. von irgendeinem Chr(I) mal einer weniger, und dafür von Chr(I+1) einer mehr.
Es wäre schön, wenn ihr mir auch noch diese Nuss knacken könntet.
Moderiert von Martok: Delphi-Tags hinzugefügt
|
|
Narses
      

Beiträge: 10183
Erhaltene Danke: 1256
W10ent
TP3 .. D7pro .. D10.2CE
|
Verfasst: Mi 11.07.12 22:52
Moin!
UweK hat folgendes geschrieben : | Aber das Problem liegt bei mir noch beim base64. Dieses habe ich nicht in meinem Delphi 2010 |
Such mal nach einer base64-Unit für Delphi, da gibt´s bestimmt was. Indy kennt auch base64, ich weiß aber nicht, ob das die Kompo ist, die du da verwendest.
UweK hat folgendes geschrieben : | Das produziert bei bestimmten in Memo1 eingetippten Strings Fehlerabbrüche in Zeile (***) schon beim Decodieren des ersten Zeichens |
Kannst du bitte mal die exakte Fehlermeldung zeigen?
cu
Narses
_________________ There are 10 types of people - those who understand binary and those who don´t.
|
|
UweK 
      
Beiträge: 52
Erhaltene Danke: 1
Win 7
Delphi Enterprise XE6
|
Verfasst: Do 12.07.12 13:46
Hallo Narses,
Kurz zuvor: Ich habe in den Testprogrammen noch eine kleine Änderung gemacht: "Memo.Text" => "Memo.Lines[0]", damit ich nicht immer die Steuerzeichen zum Zeilenwechsel mitschleppe, wer weiß, was die noch anrichten.
1. Mit IdEncoderMime/IdDecorderMime von Indy:
Es kommt nur eine ganz dünne Fehlermeldung "data error" ohne weitere Angaben. Das passiert immer in der Zeile
while Decompressor.Read(C, 1) = 1 do
z.B. (im Debugger angeschaut) bei "Normaler Text!!!!" (4 Ausrufezeichen) beim decodieren des 4. Ausrufezeichens. Bis dahin wird der String in der Variablen "Data" korrekt zusammengesetzt.
2. Alternativen
Ich habe eine Unit "base64" hier gefunden. Ist das die Unit, die du kennst? Oder haben vielleicht mehrere Autoren Units mit gleicher Funktion und gleichem Namen aber verschiedem Innenleben in Umlauf gebracht?
www.koders.com/delph...F9BD0483330D256.aspx
Mit dieser hier wird auf jeden Fall der Quatsch noch quätscher. Z.B. liefert das Encodieren des Strings Memo1 (steht schon beim Programmstart als Voreinstellung im Memo) den encodierten String eAigTQr-Nwx BQ "wA== . Der scheint keinen Sinn zu machen, weil er 1x "-" (Minus), 1x """ (Anführungszeichen oben) und 2x " " (Leerzeichen) enthält, die im Base64 Zeichensatz gar nicht enthalten sind.
Und das Wunderlichste: Ich kann zig-mal den btnEncodeClick drücken, und erhalte - logischerweise - jedes Mal im Memo2 denselben encodierten String wie angegeben. Wenn ich jetzt aber mal - ohne Veränderung des Inhalts von Memo1 - von meiner Anwendung weg zu einem anderen Programm wechsle und wieder zurück, und drücke dann erneut auf btnEncodeClick, so sieht der encodierte String in Memo2 plötzlich ganz anders aus: eAteTQorNwalBQrawA== . Und so bleibt er auch bei wiederholtem Drücken. Er enthält zwar jetzt keine unpassenden Zeichen mehr, aber ein Decodieren ist weder in der ersten noch in der zweiten Version möglich. Es tritt immer ein "Fehler bei Bereichsprüfung" auf.
=> Fragezeichen?
Danke, Gruß Uwe
|
|
Narses
      

Beiträge: 10183
Erhaltene Danke: 1256
W10ent
TP3 .. D7pro .. D10.2CE
|
Verfasst: Do 12.07.12 14:13
Moin!
UweK hat folgendes geschrieben : | Ich habe in den Testprogrammen noch eine kleine Änderung gemacht: "Memo.Text" => "Memo.Lines[0]", damit ich nicht immer die Steuerzeichen zum Zeilenwechsel mitschleppe, wer weiß, was die noch anrichten. |
Das würde ich nicht machen, es geht ja genau darum, auch nicht darstellbare Zeichen korrekt transportieren zu können.  Es sollte aber letztlich egal sein und keinen Einfluss haben.
UweK hat folgendes geschrieben : | 1. Mit IdEncoderMime/IdDecorderMime von Indy: |
Ich habe mir mal gerade diese Kompo bei der Indy-Version, die ich drauf habe, angesehen: ja, es ist ein base64-En/Decode. Immerhin.
UweK hat folgendes geschrieben : | Es kommt nur eine ganz dünne Fehlermeldung "data error" ohne weitere Angaben. |
Hm, und "dünne" Fehlermeldungen kann man nicht als Screenshot abbilden?
UweK hat folgendes geschrieben : | Das passiert immer in der Zeile
while Decompressor.Read(C, 1) = 1 do
z.B. (im Debugger angeschaut) bei "Normaler Text!!!!" (4 Ausrufezeichen) beim decodieren des 4. Ausrufezeichens. Bis dahin wird der String in der Variablen "Data" korrekt zusammengesetzt. |
In deinem vorletzen Beitrag hast du aber geschrieben, es passiere schon beim ersten Zeichen, nicht beim letzen!
UweK hat folgendes geschrieben : | 2. Alternativen
Ich habe eine Unit "base64" hier gefunden. Ist das die Unit, die du kennst? Oder haben vielleicht mehrere Autoren Units mit gleicher Funktion und gleichem Namen aber verschiedem Innenleben in Umlauf gebracht?
www.koders.com/delph...F9BD0483330D256.aspx
Mit dieser hier wird auf jeden Fall der Quatsch noch quätscher. |
Ja, das liegt daran, dass diese Unit nicht Unicode-save ist (ist übrigens genau die, die ich hier auch habe). Die kannst du nicht ohne Anpassungen verwenden.
UweK hat folgendes geschrieben : | Und das Wunderlichste: ... |
Kurz gesagt: ich halte das für ein Unicode-Problem. Schau dir mal die Methodendeklaration an, Beispiel:
Was für ein Datentyp wird dir da an den markierten Stellen angezeigt?
cu
Narses
Einloggen, um Attachments anzusehen!
_________________ There are 10 types of people - those who understand binary and those who don´t.
|
|
jaenicke
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Do 12.07.12 14:58
Die Base64 Unit, die ich gefunden hatte, ließ sich mit wenigen Änderungen umschreiben. Aber die braucht man ja nicht, da Indy das kann.
Was ich mich frage ist, warum immer wieder mit Strings als Datencontainern angefangen wird. Die sind dafür schlicht nicht gedacht. 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57:
| procedure TTestForm.btnEncodeClick(Sender: TObject); var Data: String; CompressedData: TMemoryStream; Compressor: TCompressionStream; IdEncoderMIME: TIdEncoderMIME; begin IdEncoderMime := TIdEncoderMIME.Create; try Data := Memo1.Text; CompressedData := TMemoryStream.Create; try Compressor := TCompressionStream.Create(clMax, CompressedData); Compressor.Write(PChar(Data)^, Length(Data) * SizeOf(Char)); Compressor.Free; CompressedData.Position := 0; Memo2.Text := IDEncoderMIME.Encode(CompressedData); finally CompressedData.Free; end; finally IdEncoderMIME.Free; end; end;
procedure TTestForm.btnDecodeClick(Sender: TObject); var CompressedData: TMemoryStream; Decompressor: TDecompressionStream; C: Char; Data: String; IdDecoderMIME: TIdDecoderMIME; begin CompressedData := TMemoryStream.Create; try IdDecoderMime:= TIdDecoderMIME.Create; try IdDecoderMime.DecodeBegin(CompressedData); IdDecoderMIME.Decode(Memo2.Text); IdDecoderMIME.DecodeEnd; finally IdDecoderMime.Free; end; CompressedData.Position := 0; Decompressor := TDecompressionStream.Create(CompressedData); try Data := ''; while Decompressor.Read(C, SizeOf(Char)) = SizeOf(Char) do Data := Data + C; Memo3.Text := Data; finally Decompressor.Free; end; finally CompressedData.Free; end; end; | Ausprobieren kann ich das leider nicht, da ich im Büro bin, hab das nur kurz in den Browser getippt. Aber das sollte so passen.
Für diesen Beitrag haben gedankt: UweK
|
|
Martok
      
Beiträge: 3661
Erhaltene Danke: 604
Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
|
Verfasst: Do 12.07.12 15:53
Indy deklariert(e?) übrigens irgendwo den Datentyp "Binary", der immer dem 1-Byte-String ohne Charset entspricht (meistens also AnsiString). Sie benutzen ihn nur selber nicht mehr 
_________________ "The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
|
|
Narses
      

Beiträge: 10183
Erhaltene Danke: 1256
W10ent
TP3 .. D7pro .. D10.2CE
|
Verfasst: Fr 13.07.12 10:31
Moin!
Mir fällt da gerade noch was auf:
UweK hat folgendes geschrieben : | Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8:
| MeinString:= ''; for I:= 0 to 31 do MeinString:= MeinString + Chr(255); for I:= 0 to 111 do for J:= 0 to 7 do MeinString:= MeinString + Chr(I); for I:= 928 to 1023 do MeinString:= MeinString + Chr(255); |
Meine zu verarbeitenden Strings sind immer genau 1024 Zeiichen lang, und immer von dieser Struktur mit aufsteigendem Chr(I), vorn und hinten mit Chr(255), nur gelegentliche Variationen in der Anzahl jedes Zeichens, also z.B. von irgendeinem Chr(I) mal einer weniger, und dafür von Chr(I+1) einer mehr. |
Du könntest dir auch diese "Strings" als Generator-Patterns "merken", also eine Art Code ausdenken, der beschreibt, wie man den String bildet. Ich mach mal ein Beispiel:
Quelltext Übersetzt: 32x $FF, 112x (8x Schleifenindex i), 92x $FF, 0 als Terminator
Ist jetzt nur eine Idee, kann man sicher auch noch anders angehen, aber allemal besser, als die Daten selbst zu speichern, wenn die Erzeugung selbst recht einfach ist.
cu
Narses
_________________ There are 10 types of people - those who understand binary and those who don´t.
Für diesen Beitrag haben gedankt: UweK
|
|
UweK 
      
Beiträge: 52
Erhaltene Danke: 1
Win 7
Delphi Enterprise XE6
|
Verfasst: Fr 13.07.12 12:59
Hallo allerseits,
Da habe ich ja eine ganz schöne Diskussion losgetreten. Auf jeden Fall: Problem ist gelöst!
Der Vorschlag von jaenicke mit TMemoryStream erschlägt die letzten Fehler. Wobei aber die sehr ähnliche Version mit TStringStream von Narses zuvor auch funktioniert, wenn das gewünschte Ziel ein gezippter String mit Binärzeichen ohne zusätzliche Base64 Codierung ist. Habe beide Versionen in meinem Programm durchgetestet. Warum es bei bestimmten Strings nicht klappt, wenn man das Ergebnis des Zip von Narses einfach in IdDecoderMIME steckt, ist mir zwar weiterhin ein Ministerium, macht aber nichts.
Die letzte Idee von Narses mit der selbstgemachten Codierung wäre noch ein Plan B gewesen. Aber so ist es mir lieber, denn jetzt habe ich allgemein wiederverwendbare Prozeduren für Zip mit oder ohne Base64, die vielleicht mal wieder nützlich sind.
Gruß + Danke, Uwe
|
|
jaenicke
      
Beiträge: 19312
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Fr 13.07.12 13:50
UweK hat folgendes geschrieben : | Warum es bei bestimmten Strings nicht klappt, wenn man das Ergebnis des Zip von Narses einfach in IdDecoderMIME steckt, ist mir zwar weiterhin ein Ministerium, macht aber nichts. |
Ich weiß nicht so genau warum, aber ich habe gesehen, dass da nach dem Komprimieren und vor dem Base64 etwas anderes drin steht als nach dem Dekodieren des Base64 Strings. Ob das an den Strings liegt oder an der Base64-Funktion weiß ich nicht, danach habe ich nicht geschaut. (Weil der Stream ohnehin sinnvoller ist.)
|
|
|