bytes nach int (little endian bitshift)

Legion

Mitglied
hallo!

ich hab hier ein kleines problem...
ich will eine datei auslesen. es handelt sich dabei um eine binärdatei von einem älteren spiel. ich les die datei byte-weise aus. nur leider is das gelesene material totaler mist. deshalb liegt die vermutung sehr nahe, dass es little endian ist.

leider habe ich keine ahnung von diesem bitgeshifte...
ich bräuchte folgende funktionen:

2 byte -> short
2 byte -> unsigned short (int)
4 byte -> int

also die ausgelesenen bytes sind little endian und ich glaube die resultierenden zahlen (short/int) sollen es auch sein.
folgende funktion hab ich mit der suche gefunden:

[JAVA=4]
private static int toInt(byte b[])
{
return (b[0] & 0xFF) | ((b[1] & 0xFF) << 8) | ((b[2] & 0xFF) << 16) | ((b[3] & 0xFF) << 24);
}
[/code]

die erstellt zwar, nen int in little endian, aber die beachtet doch nicht, dass die bytes little endian sind?! ich bin verwirrt...
 
Big- und little endian gibt an, in welcher Reihenfolge sich die Bytes befinden. Ein Byte ist einfach nur ein Byte.

Und was meinst du mit "gelesenes Material ist mist"... woran merkst du es denn?
 
Die Konvertierung von Big nach Little Endian ist relativ einfach:
0x4711 in BE ist in LE 0x1147.
Code:
BE	|	LE
4711	|	1147
2342	|	4223
...	|	...
Die Bytes müssen praktisch nur umgekehrt gelesen werden. Wikipedia hat dies meiner Meinung nach auch recht gut erklärt.
 
@XHelp: ja, das was du geschrieben hast, weiss ich selber. wie "gastredner" richtig gesagt hat, die bytes muss man einfach umdrehen... dass das gelesene müll ist merke ich daran, dass teilweise negative und völlig unrealistische werte für zb. breite und höhe von einem bild rauskommen.

@gastredner: ja, ich weiss ziemlich genau wo der unterschied zwischen little endian und big endian liegt. google ist mir ein begriff. wenn ich in java mit nem input stream ein byte lese, is das aber big endian und meine frage war "wie wandle ich big endian in little endian um?". soll heissen wie kehrt man die reihenfolge um mit diesen bitshift operatoren?

nur zum verständnis, angenommen in der datei steht 10011111 10101010 und diese bytes wurden als little endian geschrieben. wenn ich das nun auslese, dann behandelt das java als 11111001 01010101 ?! und das is doch falsch?! also wie kehre ich das um und wie mache ich anschließend aus den umgekehrten bytes:

2 byte -> short
2 byte -> unsigned short (int)
4 byte -> int

edit:
ich habe bisher den datainputstream benutzt, der macht genau was ich brauch zb readUnsignedShort(). da die daten aber in little endian vorliegen kommt da mist raus.
 
Zuletzt bearbeitet:
@XHelp: ja, das was du geschrieben hast, weiss ich selber.
Scheinbar nicht. Du musst die Reihenfolge der Bytes ändern und nicht die Reihenfolge von Bits.
Und du hast doch schon selber ein Beispiel gepostet, wie man es mit shift macht. Warum dann überhaupt die Frage?
Bist du dir sicher, dass du nicht einfach das Format falsch verstanden hast und wertest einfach nur falsche Daten aus?

P.S. In java sind alle primitiven Datentypen (bis auf char) vorzeichenbehaftet
 
danke für deine antwort.
ihr habt beide absolut recht. ich stand da total aufm schlauch. habs mir (zum 2. mal) bei wikipedia angeschaut. sry...

die daten lese ich schon richtig aus (denke ich) da ich da nach ner anleitung/spezifikation vorgehe. (utlima online grafik dateien, falls es jemanden interessiert...)
das code beispiel hab ich hier im forum gefunden und anscheinend is das das richtige für 4 byte -> int. leider versteh ich das mit dem bitshifts nicht 100%ig, deshalb kann ich die anderen funktionen die ich brauche nicht selber zusammenbasteln.
dass es in java kein unsigned gibt hab ich schon gemerkt, aber da gibts doch diesen "workaround", wo man einfach einen größeren typ verwendet, das würde mir auch reichen.
 
Nimm dir mal 4 bytes und mache es auf dem Papier. Dadurch wird dir der ablauf schon mal klar.
Ansonsten kannst du auch die selbe Methode verwenden, nur eben dein Byte-Array bei 2 Bytes mit noch zwei 0 auffüllen.
 
Halloechen,

also wie kehre ich das um und wie mache ich anschließend aus den umgekehrten bytes:

2 byte -> short
2 byte -> unsigned short (int)
4 byte -> int


z.B. so:

Java:
private int byteArrayLittleEndianToInt(byte[] byteArray)
{
    return ByteBuffer.wrap(byteArray).order(ByteOrder.LITTLE_ENDIAN).getInt();
}

private short byteArrayLittleEndianToShort(byte[] byteArray)
{
    return ByteBuffer.wrap(byteArray).order(ByteOrder.LITTLE_ENDIAN).getShort();
}

Gruss, FFNB.
 
Wenn du vorzeichenlose 8-Bit Werte einlesen willst (in C unsigned char bzw. uint8_t), dann musst du in Java einen größeren Datentyp (short/int/long) verwenden:
Java:
byte b = read();
int i = b & 0xFF;
Wenn du das nicht machst dann wird der vorzeichenlose Wert als vorzeichenbehaftet betrachtet und du bekommst negative Werte.
 
Java:
private static short toShort(byte b1, byte b2)
{
	return (short)((b1 & 0xFF) | ((b2 & 0xFF) << 8));
}

private static int toUnsignedShort(byte b1, byte b2)
{
	return (b1 & 0xFF) | ((b2 & 0xFF) << 8);
}

private static int toInt(byte b1, byte b2, byte b3, byte b4)
{
	return (b1 & 0xFF) | ((b2 & 0xFF) << 8) | ((b3 & 0xFF) << 16) | ((b4 & 0xFF) << 24);
}

private static long toUnsignedInt(byte b1, byte b2, byte b3, byte b4)
{
	return (b1 & 0xFF) | ((b2 & 0xFF) << 8) | ((b3 & 0xFF) << 16) | ((b4 & 0xFF) << 24);
}

also so hauts hin, die ausgelesenen werte sind nun richtig. alle funktionen hab ich noch nicht getestet, aber toUnsignedShort und toInt gehn sicher. ausserdem haben paar tests ergeben, dass man um ca. 100% schneller is wenn man die funktionen oben nimmt statt einem dataInputStream und dessen .getInt()...
wenn man den dataInputStream einem bufferedInputStream übergibt ist man immernoch um über 50% schneller.

@FerFemNemBem: schau mal hier: Converting an unsigned byte array to an integer den 2. comment an. da steht, dass der weg über den ByteBuffer ca. 8x langsamer ist. jedes bisschen performanz wär mir aber recht.
 
Code:
toUnsignedShort(b1, b2)
ist das gleiche wie
Code:
toInt(b1,b2,0,0)
. Außerdem zweifle ich an der toUnsignedInt-Methode. Bei negativen Werten wird auch long negativ sein.
 
ah ok. ja gut, ich brauch die funktion einmal für 2 byte und einmal für 4 byte, aber das könnte man dann schöner machen bzw weniger redundant, das seh ich ein.

die toUnsignedInt hab ich aus den kommentaren von dem link, den ich gepostet hab. ich hab nur die reihenfolge für little endian geändert, aber sonst is das genau diese funktion. die leute dort meinen das würde so hinhauen...
meinst du, dass wenn ein byte negativ ist, dass dann der long auch negativ ist? ich glaube, dass "& 0xFF" die bytes unsigned macht bzw. negative werte ausschließt?!
 
Die Antwort dazu findest du in der JLS 5.1.2:
A widening conversion of a signed integer value to an integral type T simply sign-extends the two's-complement representation of the integer value to fill the wider format.
Bei byte&0xFF kommt ein int raus. bei int | int kommt ein int raus. Durch das ganze shiften ist es durch aus möglich, dass da eine negative Zahl rauskommt (nämlich wenn der höhste Bit bei b4 gesetzt ist). Wenn du es stillschweigend nach long castest (denn nichts anderes machst du), dann bleibt der negative Wert erhalten. Habe es zwar nicht getestet, aber sowas wird wohl funktionieren:
Java:
private static long toUnsignedInt(byte b1, byte b2, byte b3, byte b4)
{
    return ((b1 & 0xFF) | ((b2 & 0xFF) << 8) | ((b3 & 0xFF) << 16) | ((b4 & 0xFF) << 24))&0xFFFFFFFFl;
}
Das
Code:
l
am Ende ist wichtig
 

Zurück
Oben