Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Delphi Ordentliche Komprimierungsunit (https://www.delphipraxis.net/138629-ordentliche-komprimierungsunit.html)

GreenHorn3600 13. Aug 2009 23:09


Ordentliche Komprimierungsunit
 
Hallo Wissende,

wollte mal fragen, ob jemand von euch eine ordentlche Komprimierungsunit hat, welche man sowohl in kommerziellen als auch in OS Projekten einsetzen darf.

Die ZLib funktioniert ja halbwegs, schafft schon mal 2°/oo an Komprimierung oder vergrössert gleich um 100% beim komprimieren ;-) . Wenn man dann die Datei mit einem HexEditor ansieht, sieht sie unverändert aus. Also scheint sie wohl nicht so das richtige zu sein.

Aber vielleicht kennt ja einer von euch, da was Sinnvolles :-)

Schöne Grüße
Greeny

Luckie 13. Aug 2009 23:23

Re: Ordentliche Komprimierungsunit
 
Zitat:

Zitat von GreenHorn3600
Die ZLib funktioniert ja halbwegs, schafft schon mal 2°/oo an Komprimierung oder vergrössert gleich um 100% beim komprimieren ;-) . Wenn man dann die Datei mit einem HexEditor ansieht, sieht sie unverändert aus. Also scheint sie wohl nicht so das richtige zu sein.

Kann ich mir eigentlich nicht vorstellen. Deswegen bin ichmir ziemlich sicher, dass du da irgendwas falsch machst.

GreenHorn3600 15. Aug 2009 23:23

Re: Ordentliche Komprimierungsunit
 
*push*

weiss keiner, wie man Streams ordentlich komprimieren kann? Wobei die Unit für Closed und OS geeignet sein sollte?

:stupid: :stupid: :stupid:

Luckie 15. Aug 2009 23:46

Re: Ordentliche Komprimierungsunit
 
ZLib.

Meinst du nicht auch, dass wenn sie der Maßen schlecht funktionieren würde, dass sich da nicht schon andere gemeldet hätten?

Du kannst jetzt entweder weiter nach einer Alternative suchen oder du zeigst uns mal deinen Code und wir suchen deinen Fehler.

GreenHorn3600 16. Aug 2009 00:12

Re: Ordentliche Komprimierungsunit
 
Liste der Anhänge anzeigen (Anzahl: 2)
Hallo Luckie,

dann mal viel Spass beim Suchen :-) . Habe gerade ein kleines Testprojekt zusammengezimmert, sollte aber für einen Einblick genügen.

Edit: Update der Compremierungsroutine (Position := 0 vergessen)
Delphi-Quellcode:
procedure TForm2.Compress;
var
  zc: TZCompressionStream;
  ms1: TMemoryStream;
  buff: array [0..2048] of byte;
  len: integer;
begin
  ms1 := TMemoryStream.Create;
  try
   zc := TZCompressionStream.Create(ms1, zcMax);
   try
     ms.Position := 0;
     repeat
       len := ms.Read(buff, 2048);
       zc.Write(buff, len);
     until len <> 2048;
     lRateOfCompression.Caption := Format('%.3f', [zc.CompressionRate]);
     ms.Position := 0;
     ms.CopyFrom(ms1, ms1.Size);
   finally
      zc.free;
    end;
  finally
    ms1.Free;
  end;
end;
Grüße
das GreenHorn

Dax 16. Aug 2009 00:25

Re: Ordentliche Komprimierungsunit
 
Du musst den Zielstream (glaube ich) vor'm kopieren leeren, sonst behält der die alte Länge und wird höchstens länger.

jaenicke 16. Aug 2009 04:48

Re: Ordentliche Komprimierungsunit
 
Das ist so, ja. Leeren oder die Größe danach auf die neue Größe setzen. ;-)

GreenHorn3600 16. Aug 2009 09:01

Re: Ordentliche Komprimierungsunit
 
Hallo Dax, Jänicke,

mhhh, ist nicht wirklich hilfreich. Wie ihr mit einem Blick auf den Code erkennen könnt, wird der Ausgabestream neu (also leer) erzeugt. Er wird ja auch prima gefüllt. Nur, ist dann der Inhalt reichlich unkomprimiert... Dies war ja auch meine Kritik dran, daher die Frage nach einer anderen, welche es besser kann. Übrigens, ein kleines Beispielprojekt ist angehangen, nur die Methode ist gegen die aktuelle hier aus dem obigen Post zu tauschen.

Greeny

SirThornberry 16. Aug 2009 11:09

Re: Ordentliche Komprimierungsunit
 
Zitat:

Zitat von GreenHorn3600
...Wie ihr mit einem Blick auf den Code erkennen könnt, wird der Ausgabestream neu (also leer) erzeugt...

Ich sehe nur das der MemoryStream erzeugt wird.
Ansonsten liest du aus einem anderem Stream (ms) und schreibst diesen in cz/ms1.
Anschließend kopierst du den komprimierten Stream an den Anfang des Streams aus dem du die ganze Zeit gelesen hast.
Ist dein unkomprimierter Stream also z.B. 5 MB groß und die komprimierten Daten sind 1 MB groß, so schreibst du diese 1 MB an den anfang des Ursprüngsstreams. Die restlichen 4 MB des Ursprungsstreams bleiben allerdings erhalten so das deine Datei weiterhin 5 MB groß ist. Welchen Sinn macht das?
Zudem verstehe ich deine Vorgehensweise mit der Schleife nicht. Warum verwendest du an dieser Stelle nicht bereits die CopyFrom-Methode?

alzaimar 16. Aug 2009 11:35

Re: Ordentliche Komprimierungsunit
 
Hi Greeny,

Für dich dürfte es keine geeignete Kompressions-Unit geben. Du kannst nämlich mit egal welcher Klasse nicht richtig umgehen. :thumb:
Bei mir funktioniert ZLib ganz prächtig, aber ich weiss auch, wie man die Library verwenden muss. Der Hauptfehler befindet sich übrigens in 99% der Fälle ca. 80cm vom Bildschirm entfernt und zeigt mit dem Finger auf andere. :mrgreen:

Hier eine dezent korrigierte Version, sowie eine Alternative, die etwas kompakter ist, da sie den zu komprimierenden Stream einfach per CopyFrom direkt in ZLib reinbläst, anstatt sie in 2k-Happen zu komprimieren (Meine Unit kennt übrigens kein TZCompressionStream, vielleicht liegt es auch daran):
Delphi-Quellcode:
procedure TForm2.Compress;
var
  zc: TCompressionStream;
  ms1: TMemoryStream;
  buff: array[0..2048] of byte;
  len: integer;
begin
  ms1 := TMemoryStream.Create;
  ms.position := 0; // 1.Fehler, ms war am Ende und u.g. 'read' lieferte 0 bytes.
  try
    zc := TCompressionStream.Create(clMax, ms1);
    try
      repeat
        len := ms.Read(buff, 2048);
        zc.Write(buff, len);
      until len <> 2048;
      lRateOfCompression.Caption := Format('%.3f', [zc.CompressionRate]);
    finally
      zc.free; // 2.Fehler: Der ZCompressionStream hat noch Einiges im Puffer
    end;
    ms.clear; // 3.Fehler: Alten Stream löschen
    ms.CopyFrom(ms1, 0); // 4.Fehler: ms1 komplett von Anfang an kopieren
  finally
    ms1.Free;
  end;
end;

procedure TForm2.Compress1;
var
  zc: TCompressionStream;
  ms1: TMemoryStream;

begin
  ms1 := TMemoryStream.Create;
  try
    zc := TCompressionStream.Create(clMax, ms1);
    try
      zc.CopyFrom(ms, 0);
    finally
      zc.free;
    end;
    lRateOfCompression.Caption := Format('%.3f', [zc.CompressionRate]);
    ms.clear;
    ms.CopyFrom(ms1, 0);
  finally
    ms1.free;
  end;
end;
Bevor Du dich über meine Sticheleien echauffierst, bedenke, das Du dich sehr weit aus dem Fenster gelehnt hast und offenbar über eine gehörige Portion Selbstbewustsein verfügst, denn sonst würdest Du nicht allen Ernstes als 'GreenHorn' eine dermaßen erprobte Unit wie ZLib in Frage stellen. Soviel Chuzpe muss man erstmal haben :lol:

:cheers:

jfheins 16. Aug 2009 11:43

Re: Ordentliche Komprimierungsunit
 
Zitat:

Zitat von alzaimar
Code:
    zc := TCompressionStream.Create(clMax, ms1);
    try
      zc.CopyFrom(ms, 0);
    finally
      [color=#FF0000][b]zc.free;[/b][/color]
    end;
    lRateOfCompression.Caption := Format('%.3f', [[color=#FF0000][b]zc.CompressionRate[/b][/color]]);

Das kann gutgehen ... muss aber nicht :mrgreen:

GreenHorn3600 16. Aug 2009 12:22

Re: Ordentliche Komprimierungsunit
 
Hallo alzaimar,

dank Dir. Ich bin ja auch nur ein GreenHorn. Hab aber noch ein paar fragen dazu. Mein Stream (TStream) kennt kein .Clear wie kann man das dennoch wieder säubern?

Andere Frage, beim Stream schreibst Du
Delphi-Quellcode:
zc.CopyFrom(ms, 0)
Dies ist definiert als:
Delphi-Quellcode:
function TStream.CopyFrom(Source: TStream; Count: Int64): Int64;
...
begin
  if Count = 0 then
  begin
    Source.Position := 0;
    Count := Source.Size;
  end;
Weshalb Funktioniert dann
Delphi-Quellcode:
zc.CopyFrom(ms, ms.size)
nicht?

Dank Euch,
das GreenHorn

SirThornberry 16. Aug 2009 12:38

Re: Ordentliche Komprimierungsunit
 
Wenn clear nicht vorhanden ist kann man auch einfach mit dem Property-Size die Größe auf 0 setzen.
Oder was in den meisten Fällen performanter ist - die Größe gleich auf den richtigen Wert setzen. In diesem Fall also:
Delphi-Quellcode:
procedure CompressStream(ms: TStream);
var
  zc: TCompressionStream;
  ms1: TMemoryStream;
begin
  ms1 := TMemoryStream.Create;
  try
    zc := TCompressionStream.Create(clMax, ms1);
    try
      zc.CopyFrom(ms, 0);
    finally
      zc.free;
    end;
    lRateOfCompression.Caption := Format('%.3f', [zc.CompressionRate]);
    ms.size := ms1.size;
    ms.position := 0;
    ms1.postion := 0;
    ms.CopyFrom(ms1, ms1.size);
  finally
    ms1.free;
  end;
end;
Zitat:

Weshalb Funktioniert dann...nicht?
"Nicht funktionieren" ist eine sehr schlechte Fehlerbeschreibung. Kommt eine Fehlermeldung? Passiert einfach etwas anderes als erwartet? Wenn ja: was erwartest du und was passiert tatsächlich?

GreenHorn3600 16. Aug 2009 13:14

Re: Ordentliche Komprimierungsunit
 
Hallo Wissende,

kann man diesen Code noch optimieren? Geht darum, dass der Stream aStream komprimiert wird und der komprimierte anschliessend wieder im aStream zurückgegeben wird.

Delphi-Quellcode:
procedure CompressStream(aStream: TStream);
var
  zc: TZCompressionStream;
  ms: TMemoryStream;
begin
  ms := TMemoryStream.Create;
  try
   zc := TZCompressionStream.Create(ms, zcMax);
   try
      zc.CopyFrom(aStream, 0);
    finally
      zc.Free;
    end;
    aStream.Size := 0;
    aStream.copyFrom(ms, 0);
  finally
    ms.Free;
end;
@SirThornberry: beim aStream.copyfrom(ms, ms.size), sass wohl das Problem vor dem Bildschirm. Danke.

Greeny

Klaus01 16. Aug 2009 13:29

Re: Ordentliche Komprimierungsunit
 
Vielleicht geht es so schneller?

Delphi-Quellcode:
procedure CompressStream(aStream: TStream);
var
  zc: TZCompressionStream;
  ms: TStream;
begin
  ms := (ms as TMemoryStream).Create;
  zc := TZCompressionStream.Create(ms, zcMax);
  try
    zc.CopyFrom(aStream, 0);
  finally
    zc.Free;
  end;
  aStream.Free
  aStream:=ms;
 end;
Grüße
Klaus

mirage228 16. Aug 2009 13:53

Re: Ordentliche Komprimierungsunit
 
Als Alternative zur ZLIB würde ich gerne noch Bei Google suchenTurboPower Abbrevia in den Raum werfen. Kommt sogar ohne DLL aus und kann neben ZIP auch CAB Archive verarbeiten :)

jfheins 16. Aug 2009 14:02

Re: Ordentliche Komprimierungsunit
 
Zitat:

Zitat von Klaus01
Vielleicht geht es so schneller?

Delphi-Quellcode:
procedure CompressStream(aStream: TStream);
var
  zc: TZCompressionStream;
  ms: TStream;
begin
  ms := (ms as TMemoryStream).Create;
  zc := TZCompressionStream.Create(ms, zcMax);
  try
    zc.CopyFrom(aStream, 0);
  finally
    zc.Free;
  end;
  aStream.Free
  aStream:=ms;
 end;
Grüße
Klaus

2 Fehler fallen sogar mir sofort auf:

1. ms := (ms as TMemoryStream).Create; sollte ne Exception werfen, wenn dann so:
ms := TMemoryStream.Create;

2. astream müsste als var Parameter deklariert werden. So wird der übergebene Stream freiegegeben, und der lokale nicht.

Zur Logik: Ich darf nen beliebigen Stream reinpacken, und der wird hinterrücks freigeben und durch einen Memorystream ersetzt. Als hätte der Autor überlegt "Hmmm ... wie könnte ich das programmieren, damit der Zweck zwar erfüllt wird, aber die Funktion trotzdem möglichst unerwartet reagiert?"

Klaus01 16. Aug 2009 14:20

Re: Ordentliche Komprimierungsunit
 
Zitat:

Zitat von jfheins
2. astream müsste als var Parameter deklariert werden. So wird der übergebene Stream freiegegeben, und der lokale nicht.

Meine Absicht war, dass der übergebene Stream freigegeben wird und anschließend die
Adresse des intern erzeugten Streams zugewiesen bekommt.
So könnte das unkopieren entfallen.

Im Punkt 1 gebe ich Dir recht.

Objekte die als Paramter einer Routine übergeben werden müßen nicht als
var gekennzeichnet werden damit Änderungen auch ausserhalb der Routine
wahrgenommen werden.

Grüße
Klaus

jfheins 16. Aug 2009 15:02

Re: Ordentliche Komprimierungsunit
 
Wenn man ein Objekt an eine Funktion "normal" übergibt, geschieht dies mit call-by-value. Es wird also der Zeiger, der auf das Objekt zeigt kopiert und der Funktion gegeben.

Alles was im Objekt ist, wird natürlich nicht kopiert und Änderungen wirken sich auf den Aufrufer aus.

Der Zeiger selbst wird aber immernoch kopiert - d.h. es gibt 2 Zeiger (einen beim Aufrufer und einen in der Funktion). Wenn du also auf deiesen Zeiger ein aValue.Free() ausführst wird das Orginal-Objekt zerstört.
Jetzt sagst du:
aValue := ms;
damit wird der lokalen Kopie des Zeigers die Adresse des internen Streams zugewiesen. Der äußere Zeiger bleibt unverändert. Beim Verlassen der Funktion wird der Zeiger dann auch weggeworfen.

Wer auch immer diese Funktion aufruft, hat danach einen ungültigen Zeiger und ein Speicherleck :stupid:

Der var-Parameter würde dafür sogen, dass der Zeiger nicht kopiert wird, sondern sozusagen ein Zeiger auf den Zeiger übergeben wird. Dann kannst du auch den Orginal-Zeiger ändern.

Gut ist das alles abder trotzdem nicht (weshalb sich diese Frage beim normalen Programmieren auch nicht stellt :P ). Stell dir vor, du öffnest eine Datei und willst die komprimieren. Du übergibt also nen Filestream und bekommt ... einen Memorystream zurück, in dem die komprimierte Datei steht. Der Filestream wurde freigegeben. Tolle Logik :mrgreen:

GreenHorn3600 17. Aug 2009 23:15

Re: Ordentliche Komprimierungsunit
 
Halo Mirage,

danke für den Tipp, aber das ganze sollte schon mit Bordmitteln funktionieren, oder zumindest für OS und kommerzielle Programme (kostenlos) einsetzbar sein. Und da gibt es dann halt nicht mehr viel :-(

Gut, die ZLib, komprimiert jetzt mal, aber so ordentlich auch nicht. Sie schafft da Grad mal 1,44%, aber immerhin...

BTW: @alzaimer: der TZCompress ist der Name aus der unit zLib unter D2.9 unter D2.6 ist das ganze noch ohne Z . Dafür kann er keine Memorystreams. Kennst Du hier vielleicht einen Trick, um ihm Memorystreams beizubringen (in D2.6)?

Grüße
Greeny

Muetze1 17. Aug 2009 23:33

Re: Ordentliche Komprimierungsunit
 
Da bekomm ich echt Sodbrennen...

Zitat:

Zitat von GreenHorn3600
Gut, die ZLib, komprimiert jetzt mal, aber so ordentlich auch nicht. Sie schafft da Grad mal 1,44%, aber immerhin...

Und was komprimierst du? JPEG Dateien oder RAR Archive?

Zitat:

Zitat von GreenHorn3600
BTW: @alzaimer: der TZCompress ist der Name aus der unit zLib unter D2.9 unter D2.6 ist das ganze noch ohne Z . Dafür kann er keine Memorystreams. Kennst Du hier vielleicht einen Trick, um ihm Memorystreams beizubringen (in D2.6)?

Die nutzen seit eh und je TStream und somit ist es zu allen TStream Ableitungen kompatibel. Wie kommst du zu dieser Aussage?

GreenHorn3600 17. Aug 2009 23:45

Re: Ordentliche Komprimierungsunit
 
Zu Testzwecken, hatte ich eine .CHM komprimiert, unkomprimiert 418KB komprimiert mit zLIB (Maximal) 412KB. Also, kleiner ist sie schon geworden, hätte aber da schon ein paar %'te mehr erwartet, so auf ca. 360KB, oder so...

Zu der Aussage komme ich, weil ich das akutelle Projekt mal mit D2.6 geöffnet hab, dachte mir, nimmst das Z weg und das läuft... und da bringt er mir den Fehler, daß er mit TMemory Streams nichts anfangen kann. Unter D2.9 ist dies alles kein Problem.

Schöne Grüße
Greeny

Dunkel 18. Aug 2009 00:02

Re: Ordentliche Komprimierungsunit
 
Zitat:

Zitat von GreenHorn3600
Zu Testzwecken, hatte ich eine .CHM komprimiert, unkomprimiert 418KB komprimiert mit zLIB (Maximal) 412KB. Also, kleiner ist sie schon geworden, hätte aber da schon ein paar %'te mehr erwartet, so auf ca. 360KB, oder so...

CHM = Compressed HTML Help. Das Dateiformat ist von Natur aus schon komprimiert. Dass da sonstige Komprimieralgorithmen nicht mehr viel machen können, sollte eigentlich klar sein...


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