binäre Datei auslesen
-
Danke Gregor !
Alles klar!
LG
kati
-
Hi,
also ich habe den Code mehrmals geändert und folgendes ist rausgekommen:
import java.io.*; public class Shx { private String file; public Shx(String file) { this.file = file; } public void readFileHeader() throws IOException { InputStream in = new FileInputStream(file); System.out.println("available [bytes]: " + in.available()); DataInputStream d = new DataInputStream(in); /*die ersten 8 Integerwerte auslesen - Byte Order=Big*/ int i = 0; while(i < 7) { int result = d.readInt(); System.out.println("int value: " + result); i++; } /*die nächsten 2 Integerwerte auslesen - Byte Order=Little*/ i = 0; while(i < 2) { int result = d.readInt(); System.out.println("little vorher: " + result); /*hier werden die Integerwerte konvertiert*/ byte[] big = intConvert(result); int b = 0; /*byte[] -> int*/ for(int j=0; j<big.length; j++) { b |= big[j]; } System.out.println("big nachher: " + b); i++; } in.close(); } /*konvertiert Big2Little, Little2Big (für beides geeignet)*/ public byte[] intConvert(int big) { byte[] little = new byte[4]; little[0] = (byte) (big & 0x000000ff); little[1] = (byte) ((big & 0x0000ff00) >> 8); little[2] = (byte) ((big & 0x00ff0000) >> 16); little[3] = (byte) ((big & 0xff000000) >> 24); return little; } public static void main(String[] args) { try { Shx s = new Shx("C:/intranet/Apache/htdocs/finder/ms_tmp/data/shapes/wien_bezirke.shp"); s.readFileHeader(); } catch(Exception e) { e.printStackTrace(); } } }
Folgendes kommt da raus:
C:\Java\j2sdk\Shapefiles>java Shx available [bytes]: 128972 int value: 9994 int value: 0 int value: 0 int value: 0 int value: 0 int value: 0 int value: 64486 little vorher: -402456576 big nachher: -21 /*FALSCH*/ little vorher: 83886080 big nachher: 5 /*richtig*/
Ich habe den Code mit mehreren Dateien getestet, überall bekomme ich an der mit /*richtig*/ markierten Stelle das Richtige zurück, an der als falsch markierten Stelle bekomme ich immer nur falsche Werte zurück, es sollte da lt. der Spezifikation 1000 immer stehen. Kann sein, dass es mit dem Vorzeichen zusammenhängt (in der Methode intConvert() habe ich auch schon >>> ausprobiert, haut nicht hin).
thnx
kati
-
Hi Kati,
ich weiss nicht, ob das mit mit eurer Byte-Order alles so richtig ist,
aber wenn das so passt, dann liegt der Fehler wohl hier:int b = 0; /*byte[] -> int*/ for(int j=0; j<big.length; j++) { b |= big[j]; }
So wird ja nur immer nur das unterste Byte vom int geändert.
Also bei jedem Schleifendurchlauf erst b zurecht shiften.Gruss Jockel
-
Hallo Jokel,
du hast natürlich recht... da wird der unterste Byte immer überschrieben. Bei dem richtigen Ergebnis war zufällig nur das unterste Byte nicht NULL und daher war das Ergebnis auch richtig (weil die int-Zahl allzu klein war). Da bekomme ich aber weiterhin keine richtigen Zahlen raus... So habe ich den Code modifiziert:
int b = 0; for(int j=0; j<big.length; j++) { b |= big[j]; if(j == big.length-1) break; b = b << 8; }
thnx
-
Original erstellt von Kati:
**```cpp
b |= big[j];Die Zeile macht das Problem, da falls in big[j] ein Wert über 0x7f liegt, der negativ interpretiert wird und vor dem verORen auf einen int erweitert wird, also wieder auf einen negativen int...ich üebrlege noch was man tuun kann
-
Habs
Also 1. Änderung: die intConvert-Funktion muss ein int[] zurückgeben. Damit vermeidest du, dass java Werte größer 0x7f als negative Zahlen interpretierst. Als zweite Änderung die Funktion intConvert nochmal im ganzen:
public int[] intConvert(int big) { int[] little = new int[4]; little[0] = (big & 0x000000ff); little[1] = ((big & 0x0000ff00) >> 8); little[2] = ((big & 0x00ff0000) >> 16); little[3] = ((big & 0xff000000) >> 24) & 0xff; return little; }
Das wichtige ist
a) die casts auf byte fallen weg
b) der letzte wert wird nochmal mit 0xff verUNDet, damit ein eventuelles negatives Vorzeichen wegfällt.X:\>java Shx available [bytes]: 36 int value: 9994 int value: 0 int value: 0 int value: 0 int value: 0 int value: 0 int value: 64486 little vorher: -402456576 big nachher: 1000 little vorher: 83886080 big nachher: 5
-
Hallo Triphoenix,
*g* du bist der beste. *g*
ok, also es klappts. Nur ein Paar fragen hätte ich, damit ich den Code auch wirklich verstehe:
* also int[] gebe ich deswegen zurück, weil wenn der beliebige Bytewert((big & 0x0000ff00) >> 8) etc.
größer ist als 127 (signed ?), wass bei 1000 auch zutrifft, wird die Zahl negativ interpretiert.
2^9 2^8 2^7 2^6 2^5 2^4 2^3 2^2 2^1 2^0 512 256 128 64 32 16 8 4 2 1 ---------------------------------------------------------- 1 1 1 1 1 0 1 0 0 0 ---------------------------------------------------------- | | | | | | <------ ------------------- ------------------- 240
* wie ist es mit den Vorzeichen ? Wie hier:
little[3] = ((big & 0xff000000) >> 24) & 0xff;
Wie kann ich das Vorzeichen wegtun ? Ist es mit der Version ohne verUNDen nicht getan ?
Vielen Dank nochmals
-
Original erstellt von Kati:
*** also int[] gebe ich deswegen zurück, weil wenn der beliebige Bytewert((big & 0x0000ff00) >> 8) etc.
größer ist als 127 (signed ?), wass bei 1000 auch zutrifft, wird die Zahl negativ interpretiert.
**Genau. Denn sonst kannst du in den Bytes nur die ersten 128 Werte von 0..127 darstellen. Jeder Wert darüber wird von Java als negativ interpretiert und in dieser Zeile
b |= big[j];
dann falsch verrechnet. Das Problem tritt demnach immer dann auf, wenn in der Zahl IRGENDWO ein Byte ist, wo das höchste Bit gesetzt ist. Vor der Operation macht Java nämlich alle Operanden gleichgroß, in deiesem Fall würde z.B. ein Byte-Wert von 128 = 0x80 erweitert werden auf 0xffffff80 bevor das OR angewendet wird. Indem du gleich integer benutzt verhinderst du diese Konvertierung und hast mehr Kontrolle, was Java dann am Ende verrechnet.
**
* wie ist es mit den Vorzeichen ? Wie hier:little[3] = ((big & 0xff000000) >> 24) & 0xff;
Wie kann ich das Vorzeichen wegtun ? Ist es mit der Version ohne verUNDen nicht getan ?
**Das Problem liegt hier in der Art wie Java shiftet. Man kann logisch rechtsshiften und man kann arithmetisch rechtshiften. Logisch rechtsshiften ist wie mans erwartet, nämlich dass die Bits einfach nach rechts geschoben werden und alle "freien" Bits mit 0 aufgefüllt werden. Bei arithmetischen Rechtsshiften wird je nach Vorzeichen mit 0 oder 1 aufgefüllt, bei einer negativen Zahl also mit 1. Und genau so shiftet Java um die Möglichkeit zu erhalten mit shufts schnell zu dividieren. Das ist zwar oft praktisch, nicht aber mehr, wenn man mit den rohen Zahlen arbeitet. Da nach dem shiften deine Zahl ja quasi nur in den letzten beiden Hex-Stellen stimmt und alle Stellen davor je nach Vorzeichen mit 0 oder 1 belegt sind, ist es dann eben das einfachste per & 0xff alle Stellen bis auf die letzten 2 auszunullen. So hast du dann quasi logisch Shiften simuliert und kommst auf das richtige Ergebnis.
Hoffe das hift
[ Dieser Beitrag wurde am 05.02.2003 um 15:19 Uhr von TriPhoenix editiert. ]
-
Hallo !
ok, also jetzt habe ich es halbwegs kapiert. Was das unglückliche Vorzeichen angeht, könnte ich dann also auch das >>> Operator verwenden, das das Vorzeichen nicht berücksichtigt.
public int[] intConvert(int big) { int[] little = new int[4]; little[0] = (big & 0x000000ff); little[1] = ((big & 0x0000ff00) >> 8); little[2] = ((big & 0x00ff0000) >> 16); //little[3] = ((big & 0xff000000) >> 24) & 0xff; little[3] = (big & 0xff000000) >>> 24; return little; }
Da kommt dann auch das richtige raus. So und jetzt schaue ich mir die Long(Double)-Werte an.
-
Original erstellt von Kati:
**ok, also jetzt habe ich es halbwegs kapiert. Was das unglückliche Vorzeichen angeht, könnte ich dann also auch das >>> Operator verwenden, das das Vorzeichen nicht berücksichtigt.
**Ui, man lernt nie aus Der schiebt die Bits dann roh durch die Gegend also quasi logisches Shiften? Feine Sache