PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Java: Integer nach Char (Unicode) - Problem


Spasstiger
2006-09-25, 12:30:45
Man kann mit Java ja einfach durch Casting mit (char)i einer Integer-Zahl ein Unicode-Zeichen zuweisen.
Ich schreib hier gerade einen Huffman-Codierer und bin davon ausgegangen, dass auch für jede Integer-Zahl zwischen 0 und 255 ein andere Unicode-Zeichen rauskommt. Ich erzeuge halt einen Binärbaum (nach Huffman), ermittle damit den Binärcode für jedes Zeichen in der Quelldatei und fasse jeweils 8 Bit der Binärcodes zu einem char zusammen (Umrechnung der Binärzahl in Dezimalzahl und dann Typcast nach char).
Den BufferedWriter kann man ja über die Prozedur write() auch direkt mit einer Integerzahl füttern, dabei wird die Zahl wie beim Casten nach char in das zugehörige Unicode-Zeichen umgewandelt.

Jetzt mein Problem: Alle Zahlen zwischen 128 und 159 werden zu einem Fragezeichen gecastet. Das zerhaut mir natürlich meinen Codierer. Im Binären arbeiten Encoder und Decoder einwandfrei, aber mit chars bleiben nach Encoding und anschließendem Decoding nur noch Fetzen der ursprünglichen Datei übrig.
Mit Binärzahlen kann ich die codierten Dateien aber unmöglich speichern, da dabei ja enorm viel Speicherplatz verbraucht wird, was ja genau das Gegenteil von dem wäre, was ich bezwecken möchte.

Hat jemand eine Idee, wie man die 8-bittigen Binärzählen eindeutig speichern kann, ohne dabei mehr als 8 Bit = 1 Byte zu verschwenden?

Wenn die Problemstellung nicht ganz klar sein sollte, einfach nachfragen.
/EDIT: Dritter Satz etwas eindeutiger geschrieben.

Gast
2006-09-25, 12:43:27
Java verwendet UTF-16, also ist ein char 16-Bits breit.

Und zudem http://www.joelonsoftware.com/articles/Unicode.html lesen!

Spasstiger
2006-09-25, 12:56:54
Java verwendet UTF-16, also ist ein char 16-Bits breit.
Allerdings wird ein Zeichen in einer Datei immer mit 8 Bit gespeichert (kann man mit einem Texteditor nachprüfen). Ein Unicode-Zeichen wird letztendlich als zwei Zeichen gespeichert.
Deshalb wandle ich auch immer 8 Bit zur Speicherung in ein Zeichen um (ich lasse immer solange Code erzeugen, bis dieser länger als 8 Bit ist, wandle die ersten 8 Bit in ein Zeichen zur Speicherung um und schneide dann die ersten 8 Bit ab, damits nicht zu einem Überlauf kommt, was schon bei 4 MB großen Quelldateien schnell passiert ist).

Ich werde aber mal deinen Link lesen.

Hätte schon eine Idee, wie ich mein Problem lösen kann. Ich schreibe einfach eine Prozedur, welche die Zahlen zwischen 128 und 159 z.B. nach 256 bis 287 transformiert. Ich hoffe mal, dass das resultierende Zeichen dann immer noch mit 1 Byte gespeichert wird (bei den Chars, die aus 0 bis 127 bzw. 160 bis 255 resultieren, ists jedenfalls so).

P.S.: Gibts in Java eine Möglichkeit, direkt Binärwerte in eine Datei zu schreiben? Das geschieht ja auf der Festplatte physikalisch gesehen sowieso. Oder kann man keine einzelnen Bits speichern? Dann könnte ich mir jedenfalls den ganzen Unicode-Kram ersparen.

/EDIT: Da fällt mir ein, wenn ich auch bitweise einlesen könnte, würde das meinen Codierer extrem vereinfachen und beschleunigen. Aber zum bitweise lesen und schreiben muss man wohl auf Hardwareebene gehen, oder? Was dann wieder Assembler oder ähnliches bedeutet.

Gast
2006-09-25, 13:28:45
Allerdings wird ein Zeichen in einer Datei immer mit 8 Bit gespeichert (kann man mit einem Texteditor nachprüfen).

Wirds nicht. Das ist wohl UTF-8 was Java da raushaut. Das kann bis zu 4 Bytes für ein Zeichen verwenden.

Spasstiger
2006-09-25, 13:38:45
Wirds nicht. Das ist wohl UTF-8 was Java da raushaut. Das kann bis zu 4 Bytes für ein Zeichen verwenden.
Die Zeilen
for(int i=0;i<=255;i++)
bw.write(i);
erzeugen mir eine Datei mit 256 Bytes.

Mit
for(int i=0;i<=1023;i++)
bw.write(i);
komme ich auf genau 1024 Bytes.

Also scheint wohl jedes Zeichen, mit genau einem Byte gespeichert zu werden.
Allerdings hab ich jetzt auch festgestellt, dass für alle i > 255 ebenfalls nur Fragezeichen gespeichert werden. Wie kann man denn sonst unter Java Bits sinnvoll in eine Datei speichern?
Das Auslesen von Dateien geht ja seltsamerweise anstandslos. Es kommen zwar Zeichen mit zugehörigen Integerwerten > 255 raus, aber nie mehr als 256 verschiedene. Darum hat auch jedes Zeichen in einer Datei immer 8 bit.

Ich werd mich mal näher mit der Methode write() der Klasse BufferedWriter von Java beschäftigen. Da scheint ja der Hund begraben zu liegen. Wobei die Methode write(int i) wohl nix anderes macht als (char)i in eine Datei zu schreiben.

/EDIT: In der Java-Referenz heißt es bei BufferedWriter:
public void write(int c)
Schreibt die niederwertigen 2 Bytes von c als char-Wert in dem Stream.
Das deckt sich aber nicht wirklich mit meiner Erfahrung.

AlSvartr
2006-09-25, 14:26:34
/EDIT: Da fällt mir ein, wenn ich auch bitweise einlesen könnte, würde das meinen Codierer extrem vereinfachen und beschleunigen. Aber zum bitweise lesen und schreiben muss man wohl auf Hardwareebene gehen, oder? Was dann wieder Assembler oder ähnliches bedeutet.

Mal so gefragt - vielleicht versteh ich dein Problem auch nicht richtig - aber wo ist das Problem, einfach byteweise zu lesen? Hab letztes Semester auch nen Huffman geschrieben, allerdings als ImageJ-Plugin..aber grundsätzlich sollte das doch nicht das Problem sein, oder?

Spasstiger
2006-09-25, 14:35:20
Mal so gefragt - vielleicht versteh ich dein Problem auch nicht richtig - aber wo ist das Problem, einfach byteweise zu lesen?
Brauche ich dafür andere I/O-Handler als FileReader und FileWriter/BufferedWriter? Denn soweit ich das sehe, können diese nur Integer lesen bzw. Integer, Char und String schreiben.
Und der Integer-Wert sollte theoretisch über Unicode an Char gekoppelt sein.

Wie lese ich denn sinnvollerweise Byte-weise?
Und wie bekomme ich dann je 8 bit lange Binärcodes in Bytes?
Hab in Java bisher noch nicht mit Bytes gearbeitet, deshalb meine Unkenntnis.

Bitweise, merke ich gerade, kann die Huffman-Codierung gar nicht sinnvoll funktionieren, das war ein Schnellschuss. Denn wie will man einzelne Bits noch anders codieren als in Bits?! ;) Und es interessieren ja sowieso nur die ganzen Bytes und deren Auftrittshäufigkeiten. Man muss ja analysieren, wie häufig einzelne Zeichen (im Sinne von Bytes) auftreten, um den zugehörigen Binärbaum zu erzeugen (es werden jeweils zwei "äußere" Knoten verbunden, die zusammen die niedrigste Auftittshäufigkeit haben).

/EDIT: Ah, java.io.FileInputStream und java.io.FileOutputStream, die übergeordneten Klassen von FileWriter und FileReader, können auch byteweise auslesen und schreiben. Dann sollte ich mich mal eingehend mit dem Datentyp byte beschäftigen. Gibts evtl. einfache Konvertierungsmöglichkeiten von einem Binärstring mit 8 Zeichen (String aus Einsen und Nullen) in ein byte?

/EDIT2: Einwandfrei, mit FileOutputStream werden die Zeichen eindeutig zugewiesen. Und das Casten von Integer nach byte macht die write()-Methode von FileOutputStream von alleine. Das heißt, ich muss nur meine Write-Handler anpassen. :)
... Gibt noch Fehler, muss wohl auch die Read-Handler anpassen. Wobei der decodierte Text schon halbwegs lesbar ist. ;)

/EDIT3: Es geht jetzt alles Bestens. Eine Textdatei mit mehreren Spiegel-Online-Artikeln wird fehlerfrei codiert und wieder decodiert. Danke an Alle, die versucht haben, zu helfen. :) Hab jetzt auch was über Unicode und Co. gelernt. Brauchs zwar nicht direkt für meinen Codierer, aber es ist trotzdem sinnvoll, darüber Bescheid zu wissen.

AlSvartr
2006-09-25, 15:14:51
/EDIT: Ah, java.io.FileInputStream und java.io.FileOutputStream, die übergeordneten Klassen von FileWriter und FileReader, können auch byteweise auslesen und schreiben. Dann sollte ich mich mal eingehend mit dem Datentyp byte beschäftigen. Gibts evtl. einfache Konvertierungsmöglichkeiten von einem Binärstring mit 8 Zeichen (String aus Einsen und Nullen) in ein byte?

Ich muss ein bisschen lachen..ich hab mir daran nämlich auch fürchterlich einen abgebrochen und deswegen ist meine Huffman-Variante letztlich auch ganz katastrophal langsam geworden, weil sie die ganze Zeit mit Strings arbeitet...

Ach..übrigens..wenn Du den Spaß byteweise machst, dann reicht dir byte als Code nicht aus, denn du musst ja beachten, dass der Code präfixfrei ist...natürlich solltest du deswegen später nicht gleich ganze Integer wegschreiben, dann wäre der Platzgewinn eher fraglich.. ;)

Spasstiger
2006-09-25, 15:28:27
Ach..übrigens..wenn Du den Spaß byteweise machst, dann reicht dir byte als Code nicht aus, denn du musst ja beachten, dass der Code präfixfrei ist...natürlich solltest du deswegen später nicht gleich ganze Integer wegschreiben, dann wäre der Platzgewinn eher fraglich.. ;)
Mein Codierer arbeitet intern immer noch bitweise. Die Codes sind Bitfolgen (in Form von Strings). Allerdings fasse ich immer 8 aufeinanderfolgende Bits zu einem Byte zusammen und schreibe dieses Byte in die Datei. Dazu verwende ich einen StringBuffer variabler Länge und schneide immer die ersten 8 Bit ab, sobald ich diese bearbeitet habe.
Präfixfrei ist der Code natürlich auch, das ist ja eine elementare Eigenschaft eines Huffman-Codebaums/Binärbaums.

P.S.: Kennt Java einen Datentyp für Binärzahlen? Hab bisher nix in der Richtung gefunden. Und selber eine Klasse für binäre Zahlen schreiben will ich auch nicht. ;)

/EDIT: Ich hatte eben erstmals eine Datei (MP3) mit mehr als 256 verschiedenen Zeichen. Das ist seltsam. Die Codierung funktioniert aber trotzdem fehlerfrei, obwohl ich nur 256 verschiedene Zeichen berücksichtige (hab nur ein Array etwas vergrößern müssen, allerdings werden Elemente mit Index > 255 nicht berücksichtigt). Scheint eine Eigenart von FileInputStream zu sein. Die codierte und decodierte MP3 klingt auf jeden Fall wie das Original und weist auch die gleiche Größe auf. ;) Blödsinn, waren genau 256 verschiedene Zeichen und ich hab den Fehler gemacht, meinen Arrayindex bei 1 statt bei 0 beginnen zu lassen.
Bei MP3s bringt mein Huffmann-Codierer übrigens nix, wenn man den Codebaum mit speichert. Die codierte Datei an sich wäre ein 4-kb-Cluster kleiner als die ursprüngliche Datei. Der Codebaum würde dieses Cluster wieder ausfüllen (den Algorithmus zum Codebaum speichern muss ich noch implementieren, bisher lasse ich den Codebaum noch jedesmal aus der Quelldatei erzeugen).

Gast
2006-09-25, 16:24:42
Bei MP3s bringt mein Huffmann-Codierer übrigens nix, wenn man den Codebaum mit speichert. Die codierte Datei an sich wäre ein 4-kb-Cluster kleiner als die ursprüngliche Datei. Der Codebaum würde dieses Cluster wieder ausfüllen (den Algorithmus zum Codebaum speichern muss ich noch implementieren, bisher lasse ich den Codebaum noch jedesmal aus der Quelldatei erzeugen).

MP3 macht auch schon selber Huffmann ;)

Spasstiger
2006-09-25, 16:49:10
MP3 macht auch schon selber Huffmann ;)
Winzip macht auch Huffmann, hier kann man aber meist noch was rausholen.
MP3s haben jedenfalls im Mittel einen größeren Informationsgehalt je Zeichen als Zip-Dateien. Mein Codierer spuckt mir eine Statistik aus über den Informationsgehalt (Entropie), die Redundanz(en) die mittlere Codewortlänge und den maximalen Kompressionsgrad. ;)
Kann mir bei Bedarf auch die Codes und eine Zeichenstatistik ausgeben lassen, auf Wunsch auch sortiert nach Auftrittshäufigkeit der Zeichen.
Ich sollte das Programm vielleicht mal in eine GUI packen, bisher stell ich alles direkt im Quellcode ein (einfach die nicht gewünschten Prozeduraufrufe auskommentieren ;)).

AlSvartr
2006-09-25, 18:44:49
Ich glaub ich implementier wenn ich Lust hab auch mal wieder nen Huffman, diesmal halt ne generische Variante, kein ImageJ-Plugin. Dann können wir die ja gegeneinander antreten lassen, was die Geschwindigkeit anbelangt :wink:

Spasstiger
2006-09-27, 17:51:43
Ich glaub ich implementier wenn ich Lust hab auch mal wieder nen Huffman, diesmal halt ne generische Variante, kein ImageJ-Plugin. Dann können wir die ja gegeneinander antreten lassen, was die Geschwindigkeit anbelangt :wink:
Speedmäßig ist mein Codierer ne Katastrophe, den Vergleich könntest du locker gewinnen. ;)
Für eine 5 MB große MP3 braucht mein Programm schon mehrere Minuten für Encoding und anschließendes Decoding.
Bis 2 GB große Dateien könnte ich theoretisch bearbeiten, allerdings will ich das gar nicht erst ausprobieren.