![]() |
String mit gzip (ent)zippen
Hallo miteinander.
Ich stehe vor folgendem "Problem": ich möchte gerne einen einfachen String per gzip zippen bzw. entzippen. Es sollen keine Archive oder so auf Festplatte geschrieben werden, lediglich der gzip-Algorithmus auf einen String im hauptspeicher angewendet werden. ich habe im Forum schon von der Unit ZLib gelesen und dass die gzip beherrsche. U.a. habe ich folgendes Beispiel gefunden und bereits versucht, auf mein Problem umzubauen:
Delphi-Quellcode:
Ich habe versucht, statt TFileStream die Klassen TMemoryStream und TStringStream zu verwenden, aber ohne den gewünschten Erfolg. Ich habe es einfach nicht hinbekommen. Außerdem habe ich mich schonmal mit den Abrevia-Komponenten beschäftigt, aber dort auch keine gefunden, die mir lediglich einen Datenstrom im Hauptspeicher zippt, ohne diesen anschließend auf Festplatte schreiben zu wollen. Möglich wäre das zur Not, klar, aber einfach unschön.
uses ZLib, SysUtils;
Packen: procedure Compress(InputFileName, OutputFileName: string); var InputStream, OutputStream: TFileStream; CompressionStream: ZLib.TCompressionStream; begin InputStream:=TFileStream.Create(InputFileName, fmOpenRead); try OutputStream:=TFileStream.Create(OutputFileName, fmCreate); try CompressionStream:=TCompressionStream.Create(clMax, OutputStream); try CompressionStream.CopyFrom(InputStream, InputStream.Size); finally CompressionStream.Free; end; finally OutputStream.Free; end; finally InputStream.Free; end; end; Entpacken: procedure Decompress(InputFileName, OutputFileName: string); var InputStream, OutputStream: TFileStream; DeCompressionStream: ZLib.TDeCompressionStream; begin InputStream:=TFileStream.Create(InputFileName, fmOpenRead); try OutputStream:=TFileStream.Create(OutputFileName, fmCreate); try DecompressionStream:=TDecompressionStream.Create(OutputStream); try DecompressionStream.CopyFrom(InputStream, InputStream.Size); finally DecompressionStream.Free; end; finally OutputStream.Free; end; finally InputStream.Free; end; end; Daher meine Fragen:
Vielen lieben Dank schonmal!!! |
Re: String mit gzip (ent)zippen
ZLib macht gZip ? Nein.
Zitat:
![]() PS.: Vill ist es ja auch nicht GZip was deinen Hauptspeicherstring betrifft... :mrgreen: |
Re: String mit gzip (ent)zippen
Zitat:
![]() |
Re: String mit gzip (ent)zippen
Also die normale ZLib-Unit macht kein gZip, in der ZLib.Pas (Version 1.1.3) sind auch keine
Funktionen dazu implementiert. |
Re: String mit gzip (ent)zippen
Habe nun mal die Funktionen aus deinem Link ausprobiert. Ließen sich anfangs nicht kompilieren, weil die darin verwendeten Funktionen CompressBuf und DeCompressBuf als ersten Parameter einen Zeiger erwarten. Habe das "[1]" entfernt und ein "@" vor das Input geschrieben. Dann gings. Ich habe zu Testzwecken dann zwei Memofelder auf ein Formular geklatscht und den Inhalt des einen auf Knopfdruck komprimiert und im andern angezeigt. Hat funktioniert. Daraufhin wollte ich das ganze wieder dekomprimieren und bekam dann die Fehlermeldung "Ungültige Zeigeroperation" in der Funktion DeCompressBuf. Woran kann das liegen? Bin nicht sonderlich zeigerbewandt, nur zur Info :wink: !
Zum Thema ZLib/gzip: sorry, hatte dein zitat von oben nicht aufmerksam genug gelesen :mrgreen: . |
Re: String mit gzip (ent)zippen
Zitat:
|
Re: String mit gzip (ent)zippen
Auch das hatte ich schon, aber kommt das Zeigertechnisch nicht aufs selbe raus, ob ich die Adresse des ersten Characters nehme oder die Adresse des kompletten String?
Den Pointerfehler resultiert übrigens wohl daraus, dass ich als Input/Output der Funktionen die Text-Eigenschaft der memos verwendet habe. Da beim Comprimieren Steuerzeichen entstehen, die das memo nicht darstellen kann, tauscht es die Zeichen gegen diese schwarzen Kästchen aus, die wiederum beim Einlesen einen Murks erzeugen. So oder ähnlich :mrgreen: . |
Re: String mit gzip (ent)zippen
Zitat:
Du hast doch als Parameter einen "normalen" String verwendet? @xyz liefert einen Zeiger auf die stringrefferenz @xyz[1] zeigt auf das erste Zeichen, also den Stringinhalt xyz[1] gibt das Zeichen zurück [edit] hab ausversehn editiert, statt mich zitiert ... bekomm jetzt den Originalbeitrag nich mehr komplett zusammen :freak: |
Re: String mit gzip (ent)zippen
Zitat:
EDIT: Auf alle Fälle geht nun alles wie gewünscht. Der Grund für die ungültige Zeigeroperation hat sich bestätigt. Wenn ich den komprimierten String ganz normal als String zwischenspeicher und nicht direkt in die Text-Eigenschaft des Memos klatsche, dann funktionierts. |
Re: String mit gzip (ent)zippen
Nein, XYZ ist nur ein Zeiger auf einen "Record", wo dann die Stringdaten (Referenzzähler, Längenangabe und der Stringinhalt) drin sind.
sozusagen so:
Delphi-Quellcode:
Du kannst es dir wie eine KlassenReferenz vorstellen.
TAnsiString = Record
RefCount: Integer; Len: Integer; Data: Array[1..Len+1] of AnsiChar; // +1, da noch ein #0 hinten dran ist End; Pointer(xyz) = @TAnsiString.Data @xyz zeigt also nur auf einen Pointer, wo drin steht an welcher Stelle sich der StringRecord befindet. "nur" PChar(xyz) und @xyz[1] zeigen also auf den Stringinhalt. |
Re: String mit gzip (ent)zippen
Memo.Text kannst du der Funktion nicht übergeben, da es eine Property ist. Damit können dahinter Getter/Setter liegen (was hierbei auch der Fall ist) und der Funktion als Stringspeicherplatz eine Methode bzw. deren Code zu geben ist nicht vorteilhaft. Du musst mit Zwischenwerten als Strings arbeiten oder du nutzt den
![]() |
Re: String mit gzip (ent)zippen
Liste der Anhänge anzeigen (Anzahl: 2)
2 Jahre sind vergangen und ich stehe wieder vor demselben Problem. Ich lese über ein Kartenterminal Daten von einer Smartcard ein (der eGK, wenns jemanden interessiert :wink: ), von denen es heißt, es handele sich um "gezippte XML-Dateien". "Die Basis für das Zippen ist die Software "gzip"."
Mit den bisher in diesem Thread genannten Lösungsansätzen hatte ich irgendwie kein Glück, bei der ZLib-Funktion erhalte ich wieder den Fehler "Ungültige Zeigeroperation". Ich habe auch schon die ZLib von ![]() Frage: gibt es irgendwo ein kleines Tool, das mir einen komprimierten String entpackt und anzeigt, damit ich sehen kann, ob die Daten, die ich versuche zu entpacken, überhaupt valide sind? Ich habe die gezippten Strings mal als txt-Dateien unten angehängt, vielleicht kann ja einer von euch mal sein Glück versuchen? Die Standard-Entpack-Programme erwarten solche Daten ja in einem entsprechenden Archiv mit Dateiheader usw. die bringen mich da wohl nicht wirklich weiter. Vielen Dank schonmal im Voraus! |
Re: String mit gzip (ent)zippen
Das sind keine gültigen gzip-Files gemäß
![]() Was bedeutet denn genau: Zitat:
Selbst wenn man die Bytes vor den ID-Bytes löscht, kommt nichts Gültiges raus, auch zpipe kommt nicht damit zurecht. Gammatester |
Re: String mit gzip (ent)zippen
Zitat:
Danke dass du dir die Mühe gemacht hast, die Dateien mal etwas unter die Lupe zu nehmen. Ich sehe dadurch nun zwei Möglichkeiten: 1. Was ich von der Karte ausgelesen habe, war schlichtweg Murks, der sich gar nicht erst dekomprimieren lässt. 2. Was ich von der Karte ausgelesen habe, war KEIN Murks, bedarf aber vor dem Dekomprimieren noch einer (oder diverser) Überarbeitung(en), wie Header abschneiden, Trailer abschneiden, mit 4711 multiplizieren oder weiß der Teufel. 3. Der Karteninhalt ist unbrauchbar, was ich aber bereits ausschließen kann, da das Kartenterminal auf seinem eingebauten Display bereits einige Daten der Karte anzeigen kann. Es muss es also selbst bereits geschafft haben, sie korrekt zu lesen und die Daten zu dekomprimieren. Ich habe auch mal den Kartenterminal-Hersteller kontaktiert, ob die mir da weiterhelfen können. Vielleicht sehen die den Fehler auf Anhieb... |
Re: String mit gzip (ent)zippen
Liste der Anhänge anzeigen (Anzahl: 1)
Also: wie gammatester und ich eindeutig festgestellt haben, handelt es sich bei den von mir ausgelesenen Daten um valide gzip-Konstrukte, lediglich mit einem zusätzlichen Header vor dem gzip-Header '0x1F8B08'. Schneidet man diesen ab und speichert den verbleibenden String in einer Datei mit Endung ".gz", so kann man diese z.B. mittels 7zip entpacken.
Allerdings habe ich es immernoch nicht hingekriegt, den resultierenden gzip-String in meiner Anwendung mittels ZLib zu entpacken. Der von himitsu gepostete Algorithmus
Delphi-Quellcode:
liefert mir bei DeCompressBuf immer eine ungültige Zeigeroperation. Was mache ich nur falsch???
Uses ZLib;
Function CompressString(Input: String): String; Var Buffer: Pointer; BufSize: Integer; Begin Buffer := nil; Try CompressBuf(Input[1], Length(Input), Buffer, BufSize); SetLength(Result, BufSize); Move(Buffer^, Result[1], BufSize); Finally If Buffer <> nil Then FreeMem(Buffer); End; End; Function DeCompressString(Input: String): String; Var Buffer: Pointer; BufSize: Integer; Begin Buffer := nil; Try DeCompressBuf(Input[1], Length(Input), 0, Buffer, BufSize); SetLength(Result, BufSize); Move(Buffer^, Result[1], BufSize); Finally If Buffer <> nil Then FreeMem(Buffer); End; End; Anbei noch die "richtigen" gzip-Dateien, vielleicht kann einer von euch nochmal sein Glück versuchen?!? PS: Die resultierenden Daten sind reine Testdaten einer fiktiven Person, falls sich jemand Sorgen bezüglich des Datenschutz machen sollte :wink: . |
Re: String mit gzip (ent)zippen
Hi,
es gibt ein ![]() Gruß Assertor |
Re: String mit gzip (ent)zippen
Habe ich gerade mal ausprobiert. Die Funktionen
Delphi-Quellcode:
sollten ja eigentlich genau dafür gedacht sein, einen string im gzip-Format zu entpacken. Ich bekomme jedoch immer einen "data error", keinen blassen Schimmer, warum das nicht klappt. Ich übergebe die komprimierten Daten ab dem gzip-Header "#$1F#$8B" bis zum Ende der Daten (incl. der ganzen Nullen am Schluss).
{*****************************************************************************
* GZDecompressStr * * * * pre-conditions * * s = compressed data string in gzip format * * * * post-conditions * * fileName = filename * * comment = comment * * dateTime = date/time * * * * return * * uncompressed data string * *****************************************************************************} function GZDecompressStr(const s: AnsiString; var fileName, comment: AnsiString; var dateTime: TDateTime): String; overload; function GZDecompressStr(const s: AnsiString): String; overload; |
Re: String mit gzip (ent)zippen
Hi Infect,
Zitat:
Zitat:
Delphi-Quellcode:
Das ging jetzt aber in den Bereich "Grundlagen" von Delphi und hat wenig mit gzip zu tun ;)
procedure TForm1.Button1Click(Sender: TObject);
var InStream, OutStream: TMemoryStream; begin InStream := TMemoryStream.Create; OutStream := TMemoryStream.Create; try InStream.LoadFromFile('D:\Downloads\VersichertenDaten.gz'); InStream.Position := 0; GZDecompressStream(InStream, OutStream); OutStream.Position := 0; mmo1.Lines.LoadFromStream(OutStream); // mmo1 ist ein Memo auf dem Testform finally OutStream.Free; InStream.Free; end; end; Gruß Assertor |
Re: String mit gzip (ent)zippen
Also zu allererstmal die Frohebotschaft, dass es nun endlich funktioniert! Ich habe vorher immer mit dem TStringStream gearbeitet, statt TMemoryStream, und das hat irgendwie nicht so funktioniert, wie ich mir das vorgestellt habe. Aber so, wie du es beschrieben hast, klappt es auch bei mir.
Nun aber zum Thema AnsiString und Binärdaten etc.: wenn das ganze Probleme mit Codepages usw. geben kann, wieso arbeiten dann die entsprechenden Kompressionsfunktionen mit den gleichen Datentypen?
Delphi-Quellcode:
Es wird ein ganz normaler String übergeben, der mit gzip komprimiert werden soll, und man erhält anschließend einen komprimierten AnsiString im gzip-Format, der ja auch nichts anderes als Binärdaten enthält, oder?!? Wo liegt der Denkfehler?
{*****************************************************************************
* GZCompressStr * * * * pre-conditions * * s = uncompressed data string * * fileName = filename * * comment = comment * * dateTime = date/time * * * * return * * compressed data string in gzip format * *****************************************************************************} function GZCompressStr(const s: String; const fileName, comment: AnsiString; dateTime: TDateTime): AnsiString; overload; function GZCompressStr(const s: String): AnsiString; overload; Aber erstmal Danke, dass du mir da auf die Sprünge geholfen hast, nun geht es endlich so, wie ich mir das vorgestellt habe!!! |
Re: String mit gzip (ent)zippen
Hi Infect,
Zitat:
Zitat:
Das Probleme ist aber: Wie bekommst Du die Daten unverändert in und aus dem String. In Delphi 2009 wurde deswegen der RawByteString eingeführt, damit unter keinen Umständen Veränderungen am String durchgeführt werden. Dies erfolgt im Hintergrund durch die Festlegung der CodePage auf:
Delphi-Quellcode:
Genau darin liegt/lag aber das Problem von manchem Code vor Delphi 2009: Der AnsiString wurde zum Speichern von Daten verwendet, was sich bei der Umstellung von Source und Komponenten in Delphi 2009 rächte. Zwei Hauptprobleme dabei sind: 1) Konvertierung zur Laufzeit / Compiler-Magic und 2) die Länge von Chars (wegen der damalig festen Annahme von 1 Char = 1 Byte).
type
RawbyteString = type AnsiString($ffff); Bei jeder Zuweisung eines AnsiString kannst Du nicht sicher sein, daß die CodePage nicht abweicht und somit eine Konvertierung des Strings durchgeführt wird. Wenn der String nun Daten hält, werden diese ungültig. Wenn Du, wie in meinem Beispielcode, direkt am Stream arbeitest umgehst Du diese Probleme. Natürlich kannst Du auch eine Funktion schreiben, die die Daten in einem AnsiString/RawByteString einliest und dann die zLib Funktionen für AnsiStrings verwenden. Aber da wären einige Sachen wegen oben genannter Probleme zu beachten. Gruß Assertor |
Re: String mit gzip (ent)zippen
Liste der Anhänge anzeigen (Anzahl: 1)
Danke für die Aufklärung! Auch wenn ich ehrlich gesagt nicht alles 100%ig verstanden habe, so sehe ich ein, dass das ganze sehr fehlerträchtig ist. Daher habe ich nun meine Anwendung auf Streams umgestellt und an sich funktioniert alles bestens, nur habe ich jetzt ein neues Problem (ich könnte echt druchdrehen!):
In einem weiteren gzip-komprimierten String-Container wurden zwei XML-Konstrukte unmittelbar hintereinander gehängt. Und zwar in dieser Art: Zitat:
Woran liegt das denn nun schon wieder??? Probier es selbst mal aus, ich hab dir eine entsprechende Datei mal angehängt. |
Re: String mit gzip (ent)zippen
Mit meinen gzio-Routinen funktioniert's. Vielleicht ein Bug in DelphiZLib.123?
Gruß Gammatester |
Re: String mit gzip (ent)zippen
Da hast du recht, über deine gzio-Unit hat das ganze funktioniert. Da ich aber ungern über den Umweg einer Datei arbeiten will, würde ich gerne was anderes dafür verwenden. Deine gzdbuf werde ich nun auch nochmal testen, aber ich muss ehrlich sagen: das Handling der ZLibEx-Units fällt mir persönlich deutlich leichter, da die eben "Delphi-like" sind. Aber ist ja auch kein Wunder, deine Units sollen ja nur ein 1 zu 1 Port der C Routinen (mit gewissen Erweiterungen) sein, wenn ich das richtig verstanden hab.
|
Re: String mit gzip (ent)zippen
So, nun habe ich endlich herausgefunden, warum das mit deiner gzdbuf-Unit bisher nicht funktionierte. Ich habe der Funktion gzb_init als Buffer ein dynamisches Array übergeben, kein statissches wie in deinem Beispiel. Und dynamische Arrays beginnen ja mit dem Index 0 und nicht wie bei dir mit 1.
Was müsste ich ändern, damit das ganze auch mit dynamischen Arrays klappt? Auf jeden Fall bin ich nun wieder einen Schritt weiter. |
Re: String mit gzip (ent)zippen
Zitat:
Delphi-Quellcode:
Gruß Gammatester
procedure TForm1.Button1Click(Sender: TObject);
var ibuf: array of byte; f: file; gz: gzbuf; i: integer; s: string; begin assignfile(f,'vsd.gz'); system.reset(f,1); setlength(ibuf,5000); blockread(f,ibuf[0],1242); gz := gzb_init(pBytef(@ibuf[0]),1242); // dies ist der interessante Teil s := ''; while not gzb_eof(gz) do begin i := gzb_getc(gz); if i<0 then break; if (i>0) and (i<256) then s := s+char(i); end; richedit1.Text := s; end; |
Re: String mit gzip (ent)zippen
:dancer2: Juhu, so funktioniert das ganze! :dancer2:
In deinem Beispielprogramm von letzter Woche sah die Wertüberprüfung von i aber noch so aus:
Delphi-Quellcode:
Ich vermute mal, du wolltest damit nicht druckbare Steuerzeichen überspringen. Das ist in meinem Fall gewünscht und daher würde ich das so belassen.
if (i > 31) and (i < 256) then
Gut, dann hätten wirs ja. Thread darf als gelöst betrachtet werden. Und nochmals danke an alle Beteiligten! :cheers: |
Alle Zeitangaben in WEZ +1. Es ist jetzt 21:56 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