Liest Java Ganzzahlen in Little Endian oder Big Endian?

94

Ich frage, weil ich einen Byte-Stream von einem C-Prozess an Java sende. Auf der C-Seite hat die 32-Bit-Ganzzahl das LSB als erstes Byte und das MSB als 4. Byte.

Meine Frage lautet also: Was ist auf der Java-Seite Endian auf der Java-Seite, wenn wir das vom C-Prozess gesendete Byte lesen ?

Eine Folgefrage: Wenn der Endian auf der Java-Seite nicht mit dem gesendeten übereinstimmt, wie kann ich zwischen ihnen konvertieren?

hhafez
quelle
1
Hier ist meine Mnemonik dafür, damit ich nicht vergesse: Java ist keine Hardware, sondern virtuell und die Sprache des Internets. Die Netzwerkbyte-Reihenfolge ist Big Endian . Daher ist Java Big Endian .
Truthadjustr

Antworten:

66

Verwenden Sie die Netzwerkbyte-Reihenfolge (Big Endian), die ohnehin mit Java identisch ist. Siehe man htons für die verschiedenen Übersetzer in C.

Egil
quelle
Ich bin jetzt nicht in meiner Linux-Box, aber ist htons eine der Standardbibliotheken?
Hhafez
1
htons ist fast überall verfügbar, aber nicht in ISO C.
MSalters
1
Wenn Sie etwas anderes als die Netzwerkbyte-Reihenfolge verwenden müssen, rollen Sie entweder Ihre eigenen mit bitweisen Operatoren oder verwenden die verschiedenen Versionen von java.nio.Buffer
Darron
1
Laut seiner Manpage ist es in POSIX.1 definiert, daher sollte es so ziemlich überall verfügbar sein. Und ich erinnere mich an die Verwendung in Win32, also nicht nur auf POSIX-Systemen.
Joachim Sauer
47

Ich bin hier über Google gestolpert und habe meine Antwort bekommen, dass Java Big Endian ist .

Beim Lesen der Antworten möchte ich darauf hinweisen, dass Bytes tatsächlich eine Endian-Reihenfolge haben, obwohl Sie, wenn Sie sich nur mit „Mainstream“ -Mikroprozessoren befasst haben, diese wahrscheinlich nie als Intel, Motorola und Zilog kennengelernt haben waren sich über die Verschiebungsrichtung ihrer UART-Chips einig und dass MSB eines Bytes 2**7und LSB 2**0in ihren CPUs sein würden (ich habe die FORTRAN-Power-Notation verwendet, um zu betonen, wie alt dieses Zeug ist :)).

Ich bin vor mehr als 20 Jahren auf dieses Problem mit einigen seriellen Downlink-Daten von Space Shuttle-Bits gestoßen, als wir eine 10.000-Dollar-Schnittstellenhardware durch einen Mac-Computer ersetzt haben. Es gibt einen NASA Tech Brief, der vor langer Zeit darüber veröffentlicht wurde. Ich habe einfach eine Nachschlagetabelle mit 256 Elementen verwendet, wobei die Bits umgekehrt ( table[0x01]=0x80usw.) waren, nachdem jedes Byte aus dem Bitstrom verschoben wurde.

WB Greene
quelle
Toller Einblick! Ich habe diese Frage und keine Antworten im Web.
Xolve
Wenn einer von ihnen öffentlich ist, könnten Sie den NASA-Tech-Brief (und die seriellen Downlink-Daten des Space-Shuttle-Bits), über den Sie sprechen, verknüpfen? wäre faszinierend, ich habe so etwas noch nie gesehen.
n611x007
3
Bitweise Endianness kommt auch bei Komprimierungsformaten ins Spiel, die eine Form der Huffman-Codierung verwenden (dh alle). Für zusätzlichen Spaß ist JPEG "bitweises Big-Endian" (dh das höchstwertige Bit ist das "erste" Bit) und LZ ist "bitweises Little-Endian". Ich habe einmal an einem proprietären Komprimierungsformat gearbeitet, das beide Formate unter der Haube verwendete. Oh, das hat Spaß gemacht ...
user435779
Nachdem ich in kleinen Schritten angefangen hatte, dachte ich, dass das lange Zeit Endianess war.
Roy Falk
20

In Java gibt es keine vorzeichenlosen Ganzzahlen. Alle ganzen Zahlen sind signiert und in Big Endian.

Auf der C-Seite hat jedes Byte das LSB am Anfang links und das MSB am Ende.

Es hört sich so an, als würden Sie LSB als am wenigsten signifikantes Bit verwenden, oder? LSB steht normalerweise für das niedrigstwertige Byte. Endianness basiert nicht auf Bits, sondern auf Bytes.

So konvertieren Sie ein vorzeichenloses Byte in eine Java-Ganzzahl:

int i = (int) b & 0xFF;

So konvertieren Sie von vorzeichenlosem 32-Bit-Little-Endian in Byte [] nach Java Long (von oben, nicht getestet):

long l = (long)b[0] & 0xFF;
l += ((long)b[1] & 0xFF) << 8;
l += ((long)b[2] & 0xFF) << 16;
l += ((long)b[3] & 0xFF) << 24;
Jonas Elfström
quelle
Ich habe gerade festgestellt, dass: $ also, wie soll ich diesen nicht signierten kleinen Endian an meinen Java-Prozess senden, um ihn richtig zu lesen?
Hhafez
Was ich mit dem Start meine, ist, dass lsb am Anfang der 4 Bytes steht (es ist ein vorzeichenloses 32-Bit-Int), also meinte ich das niedrigstwertige Byte
hhafez
Auch ich konvertiere von C -> Java nicht von Java -> C :)
hhafez
Ihr Code funktioniert einwandfrei, solange Sie das Semikolon nach 0xFF in den letzten drei Zeilen entfernen. Ich würde es selbst bearbeiten, aber das ist eine Änderung von weniger als 6 Zeichen.
Elch Moral
1
Es dauerte fast 8 Jahre, aber schließlich entdeckte jemand den Syntaxfehler. Danke @MooseMorals :)
Jonas Elfström
12

Es gibt keine Möglichkeit, dies in Java zu beeinflussen, da es keine (direkte Nicht-API-) Möglichkeit gibt, einige Bytes direkt in ein Int in Java abzubilden.

Jede API, die dies oder ähnliches tut, definiert das Verhalten ziemlich genau, daher sollten Sie die Dokumentation dieser API nachschlagen.

Joachim Sauer
quelle
3
Oh sicher ist es. Binäre Mathematik (&, |, << usw.) funktioniert gut mit Bytes und Ints. Es ist ziemlich einfach, beliebige Bytes in eine ganze Zahl zu stecken.
Herms
8
Wenn Sie dies tun, können Sie immer noch nicht sagen, welche Endianess Ihre JVM intern verwendet.
Darron
4
Ja, aber selbst dort ordnen Sie nicht direkt zu. Sie verwenden eine Arithmetik, die genau das tut, was Sie sagen. Es gibt keine Mehrdeutigkeit. In C können Sie immer ein "Byte *" in ein "langes *" umwandeln und die Referenzierung aufheben. Dann müsstest du dich um Endianess kümmern. In Java gibt es keine direkte, mehrdeutige Möglichkeit, dies zu tun.
Joachim Sauer
Ah ich sehe. Sie haben über die Besetzung gesprochen, nicht über die binäre Mathematik. Ja, in diesem Fall hast du recht.
Herms
10
+1 für "Dokumentation nachschlagen", aber HINWEIS: Der 1. Satz ist nicht mehr korrekt, da das NIO-Paket heutzutage ByteBuffer bietet, mit dem Bytes Primitiven zugeordnet werden können und in dem Sie die Bytereihenfolge ändern können. Siehe ByteBuffer und ByteOrder
user85421
3

Ich würde die Bytes einzeln lesen und sie zu einem langen Wert kombinieren . Auf diese Weise steuern Sie die Endianness und der Kommunikationsprozess ist transparent.

Wouter Lievens
quelle
Möchtest du kommentieren, warum du mich abwählst?
Wouter Lievens
denn selbst wenn ich jedes Byte einzeln lesen würde, wäre die Endianess des gesendeten Bytes falsch, so dass ich es konvertieren müsste
hhafez
23
Endianness eines Bytes? Was zum Teufel ist das? Wörter sind empfindlich gegenüber Endianness, einzelne Bytes nicht.
Wouter Lievens
3
@hhafez Das ist nicht wahr, Bytes haben keine Endianess, was uns betrifft, wenn Sie Byte für Byte lesen. Sie, der Programmierer, sind dafür verantwortlich, die Bytes der richtigen Stelle zuzuweisen. Genau das macht DataInputStream, es setzt einfach die Bytes auf Big-Endian-Weise unter den Hauben zusammen.
Nr.
2
@WouterLievens: Ich bin auf einige E / A-Geräte gestoßen (z. B. einen Echtzeituhr-Chip), die aus irgendeinem Grund Daten im bitumgekehrten Format senden. Nach dem Empfang von Daten von ihnen ist es notwendig, die Bits in jedem Byte umzukehren. Ich stimme Ihnen jedoch zu, dass die Endianität von Bytes im Allgemeinen kein Problem darstellt, es sei denn, man muss sich mit bestimmten seltsam gestalteten Hardwareteilen befassen.
Supercat
3

Wenn es zu dem von Ihnen verwendeten Protokoll passt, sollten Sie einen DataInputStream verwenden, bei dem das Verhalten sehr gut definiert ist .

Ilja Preuß
quelle
1
Er kann das nur tun, wenn sein Protokoll dieselbe Endianness verwendet.
Wouter Lievens
Ich habe den Link repariert und ihn so geändert, dass er auf Java 9, die aktuelle Version, verweist. Die betreffende API wurde jedoch in Java 1.0 eingeführt.
Jens Bannmann
2

Java ist 'Big-Endian' wie oben erwähnt. Das bedeutet, dass sich das MSB eines int links befindet, wenn Sie den Speicher untersuchen (zumindest auf einer Intel-CPU). Das Vorzeichenbit befindet sich auch im MSB für alle Java-Integer-Typen.
Das Lesen einer 4-Byte-Ganzzahl ohne Vorzeichen aus einer Binärdatei, die von einem Little-Endian-System gespeichert wird, erfordert in Java einige Anpassungen. ReadInt () von DataInputStream erwartet das Big-Endian-Format.
Hier ist ein Beispiel, das einen vorzeichenlosen 4-Byte-Wert (wie von HexEdit als 01 00 00 00 angezeigt) in eine Ganzzahl mit dem Wert 1 liest:

 // Declare an array of 4 shorts to hold the four unsigned bytes
 short[] tempShort = new short[4];
 for (int b = 0; b < 4; b++) {
    tempShort[b] = (short)dIStream.readUnsignedByte();           
 }
 int curVal = convToInt(tempShort);

 // Pass an array of four shorts which convert from LSB first 
 public int convToInt(short[] sb)
 {
   int answer = sb[0];
   answer += sb[1] << 8;
   answer += sb[2] << 16;
   answer += sb[3] << 24;
   return answer;        
 }
Donald W. Smith
quelle
Worauf bezieht sich "oben angegeben"? Die Reihenfolge, in der SO-Antworten angezeigt werden, kann variieren.
LarsH
0

Java Force in der Tat Big Endian: https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.11

user12482548
quelle
3
Hier geht es um die Endianness der Bytecode-Anweisungen, nicht um die Endianness der Daten zur Laufzeit.
Kaya3
Ich stimme ab. Dieses Snippet byte[] bbb = ByteBuffer.allocate(4).putFloat(0.42f).array();hat ein byteArray erzeugt, das das Gegenteil von dem ist, was ich C/C++produziert habe. Daher wirkt sich die große Endianness von Java auch in den Daten zur Laufzeit aus.
Truthadjustr