Konvertieren Sie 4 Bytes in int

73

Ich lese eine Binärdatei wie folgt:

InputStream in = new FileInputStream( file );
byte[] buffer = new byte[1024];
while( ( in.read(buffer ) > -1 ) {

   int a = // ??? 
}

Was ich tun möchte, um bis zu 4 Bytes zu lesen und daraus einen int-Wert zu erstellen, aber ich weiß nicht, wie es geht.

Ich habe das Gefühl, ich muss 4 Bytes gleichzeitig greifen und eine "Byte" -Operation ausführen (wie >> << >> & FF und ähnliches), um das neue Int zu erstellen

Was ist die Redewendung dafür?

BEARBEITEN

Hoppla, das ist etwas komplexer (um es zu erklären).

Ich versuche, eine Datei zu lesen (kann ASCII, binär sein, spielt keine Rolle) und die Ganzzahlen zu extrahieren, die sie möglicherweise hat.

Angenommen, der binäre Inhalt (in Basis 2):

00000000 00000000 00000000 00000001
00000000 00000000 00000000 00000010

Die ganzzahlige Darstellung sollte sein 1, 2oder? : - / 1 für die ersten 32 Bits und 2 für die verbleibenden 32 Bits.

11111111 11111111 11111111 11111111

Wäre -1

und

01111111 11111111 11111111 11111111

Wäre Integer.MAX_VALUE ( 2147483647 )

OscarRyz
quelle

Antworten:

71

ByteBuffer verfügt über diese Funktion und kann sowohl mit kleinen als auch mit großen Endian-Ganzzahlen arbeiten.

Betrachten Sie dieses Beispiel:


//  read the file into a byte array
File file = new File("file.bin");
FileInputStream fis = new FileInputStream(file);
byte [] arr = new byte[(int)file.length()];
fis.read(arr);

//  create a byte buffer and wrap the array
ByteBuffer bb = ByteBuffer.wrap(arr);

//  if the file uses little endian as apposed to network
//  (big endian, Java's native) format,
//  then set the byte order of the ByteBuffer
if(use_little_endian)
    bb.order(ByteOrder.LITTLE_ENDIAN);

//  read your integers using ByteBuffer's getInt().
//  four bytes converted into an integer!
System.out.println(bb.getInt());

Hoffe das hilft.

Tom
quelle
34

Wenn Sie sie bereits in einem Byte [] -Array haben, können Sie Folgendes verwenden:

int result = ByteBuffer.wrap(bytes).getInt();

Quelle: hier

iTEgg
quelle
28

Sie sollten es in eine Funktion wie diese einfügen:

public static int toInt(byte[] bytes, int offset) {
  int ret = 0;
  for (int i=0; i<4 && i+offset<bytes.length; i++) {
    ret <<= 8;
    ret |= (int)bytes[i] & 0xFF;
  }
  return ret;
}

Beispiel:

byte[] bytes = new byte[]{-2, -4, -8, -16};
System.out.println(Integer.toBinaryString(toInt(bytes, 0)));

Ausgabe:

11111110111111001111100011110000

Dies sorgt dafür, dass keine Bytes mehr zur Verfügung stehen und negative Bytewerte korrekt behandelt werden.

Mir ist keine Standardfunktion dafür bekannt.

Zu berücksichtigende Punkte:

  1. Endianness: Verschiedene CPU-Architekturen ordnen die Bytes, aus denen ein Int besteht, in unterschiedlicher Reihenfolge an. Abhängig davon, wie Sie zunächst auf das Byte-Array gekommen sind, müssen Sie sich möglicherweise darüber Gedanken machen. und

  2. Pufferung: Wenn Sie jeweils 1024 Bytes erfassen und eine Sequenz bei Element 1022 starten, erreichen Sie das Ende des Puffers, bevor Sie 4 Bytes erhalten. Es ist wahrscheinlich besser, einen gepufferten Eingabestream zu verwenden, der den gepufferten automatisch ausführt, sodass Sie ihn nur readByte()wiederholt verwenden können und sich sonst keine Sorgen machen müssen.

  3. Trailing Buffer: Das Ende der Eingabe kann je nach Quelle eine ungerade Anzahl von Bytes (nicht ein Vielfaches von 4) sein. Wenn Sie jedoch zunächst die Eingabe erstellen und ein Vielfaches von 4 "garantiert" (oder zumindest eine Voraussetzung) ist, müssen Sie sich möglicherweise nicht damit befassen.

Um den Punkt der Pufferung weiter zu erläutern, betrachten Sie Folgendes BufferedInputStream:

InputStream in = new BufferedInputStream(new FileInputStream(file), 1024);

Jetzt haben Sie eine InputStream, die automatisch 1024 Bytes gleichzeitig puffert, was viel weniger umständlich ist. Auf diese Weise können Sie glücklich 4 Bytes gleichzeitig lesen und müssen sich nicht um zu viele E / A sorgen.

Zweitens können Sie auch verwenden DataInputStream:

InputStream in = new DataInputStream(new BufferedInputStream(
                     new FileInputStream(file), 1024));
byte b = in.readByte();

oder auch:

int i = in.readInt();

und mach dir intüberhaupt keine Sorgen darüber, s zu konstruieren .

Cletus
quelle
Ich muss nur berücksichtigen, dass mein Array möglicherweise nicht die exakten % 4Bytes liest, oder?
OscarRyz
Wenn die Länge des Arrays nicht% 4 ist, können Sie die verbleibenden Bytes mit 0 auffüllen. (Da x | 0: = x und 0 << n: = 0).
Pindatjuh
3
Ein großes Problem mit Ihrem Code - der Bytetyp von Java ist SIGNED. Wenn also das oberste Bit eines Bytes gesetzt ist, setzt Ihr Code auch alle oberen Bits im resultierenden int. Sie müssen die oberen Bits jedes Bytes maskieren, bevor Sie verschieben und sprechen, z. B.(bytes[0] & 0xff) | ((bytes[1] & 0xff) << 8) | ...
Chris Dodd
1
Ich hasse es, das zu sagen, aber Ihre Offset-Unterstützung ist völlig kaputt. Siehe ideone.com/uCpovu , wo ich auch das Update habe.
Quantum
1
Vielen Dank für das Code-Snippet, ich sollte hier auf einen Fehler hinweisen - ret |= (int)bytes[i] & 0xFF;sollte es wirklich sein ret |= (int)bytes[i + offset] & 0xFF;- sonst wird der Offset-Parameter komplett ignoriert.
Ying
17

sehen Sie einfach, wie DataInputStream.readInt () implementiert wird;

    int ch1 = in.read();
    int ch2 = in.read();
    int ch3 = in.read();
    int ch4 = in.read();
    if ((ch1 | ch2 | ch3 | ch4) < 0)
        throw new EOFException();
    return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0));
Santhosh Kumar Tekuri
quelle
8
Es ist zu beachten, dass dies für Big-Endian-geordnete Bytes gilt, bei denen die Unterstützung für Little nur eine kleine Änderung erfordert: return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + (ch1) << 0));
Paul Gregoire
Es ist nicht richtig. Wenn beispielsweise das 4. Byte gleich -1 ist und andere 0 sind, ist Ihr Ergebnis -1, sollte aber 255 sein. Int k = ((Byte) -1) << 0; System.err.println (k); // -1
Mikhail Ionkin
@MikhailIonkin Ihr Kommentar ist falsch und dieser Code ist korrekt. in.read () gibt kein Byte zurück. In diesem Fall würde eine Vorzeichenerweiterung auftreten, wenn sie in einer int-Variablen gespeichert würde. In.read () gibt jedoch das nächste Byte des Streams zurück, das in die Zeichenerweiterung int WITHOUT konvertiert wurde. Wenn das nächste Byte des Streams 0xFF ist, würde in.read () 0x000000FF zurückgeben. In.read () gibt -1 nur dann zurück, wenn Sie das Ende des Streams erreicht haben.
Craig Parton
@CraigParton ja, aber die Frage ist, wie man 4 Bytes konvertiert , nicht 4 Ints
Mikhail Ionkin
5

Der einfachste Weg ist:

RandomAccessFile in = new RandomAccessFile("filename", "r"); 
int i = in.readInt();

-- oder --

DataInputStream in = new DataInputStream(new BufferedInputStream(
    new FileInputStream("filename"))); 
int i = in.readInt();
Taylor Leese
quelle
1
vorausgesetzt, seine Binärdatei enthält Big Endian signierte Ints. sonst wird es scheitern. entsetzlich. :)
stmax
4

versuchen Sie so etwas:

a = buffer[3];
a = a*256 + buffer[2];
a = a*256 + buffer[1];
a = a*256 + buffer[0];

Dies setzt voraus, dass das niedrigste Byte an erster Stelle steht. Wenn das höchste Byte zuerst kommt, müssen Sie möglicherweise die Indizes austauschen (von 0 auf 3 gehen).

Grundsätzlich multiplizieren Sie für jedes Byte, das Sie hinzufügen möchten, zuerst a mit 256 (was einer Verschiebung nach links mit 8 Bit entspricht) und fügen dann das neue Byte hinzu.

stmax
quelle
Obwohl ich Andrey konzeptionell zustimme, würde ich hoffen, dass jeder Abstiegs-Compiler das herausfindet und es für Sie repariert. Zu diesem Zweck ist << jedoch klarer.
Bill K
@Andrey: fair zu sein, wird die Java - Compiler wahrscheinlich übersetzen x * 256in x << 8automatisch.
Cletus
hängt von der Qualität des Compilers ab :)
Andrey
Es liegt nicht an dem "schnelleren" Code, den Sie verwenden sollten <<, sondern an der Lesbarkeit. Durch die Verwendung <<ist klar, dass wir eher Bitoperationen als Multiplikationen ausführen. Tatsächlich würde ich sogar das +s in |s ändern
Justin
1
for (int i = 0; i < buffer.length; i++)
{
   a = (a << 8) | buffer[i];
   if (i % 3 == 0)
   {
      //a is ready
      a = 0;
   }       
}
Andrey
quelle
1

Sie können BigInteger auch für Bytes variabler Länge verwenden. Sie können es in Long, Integer oder Short konvertieren, je nachdem, was Ihren Anforderungen entspricht.

new BigInteger(bytes).intValue();

oder um die Polarität zu bezeichnen:

new BigInteger(1, bytes).intValue();
Jamel Toms
quelle
0

Zum Lesen von 4 Bytes ohne Vorzeichen als Ganzzahl sollten wir eine lange Variable verwenden, da das Vorzeichenbit als Teil der vorzeichenlosen Zahl betrachtet wird.

long result = (((bytes[0] << 8 & bytes[1]) << 8 & bytes[2]) << 8) & bytes[3]; 
result = result & 0xFFFFFFFF;

Dies ist gut funktionierte Funktion getestet

Mounir
quelle
0

Der folgende Code liest 4 Bytes von array(a byte[]) an Position indexund gibt a zurück int. Ich habe den größten Teil des Codes aus den anderen Antworten auf Java 10 und einigen anderen Varianten ausprobiert, die ich mir ausgedacht habe.

Dieser Code verwendet die geringste CPU-Zeit, weist jedoch a zu, ByteBufferbis die JIT von Java 10 die Zuordnung beseitigt.

int result;

result = ByteBuffer.
   wrap(array).
   getInt(index);

Dieser Code ist der Code mit der besten Leistung, der nichts zuweist. Leider verbraucht es 56% mehr CPU-Zeit als der obige Code.

int result;
short data0, data1, data2, data3;

data0  = (short) (array[index++] & 0x00FF);
data1  = (short) (array[index++] & 0x00FF);
data2  = (short) (array[index++] & 0x00FF);
data3  = (short) (array[index++] & 0x00FF);
result = (data0 << 24) | (data1 << 16) | (data2 << 8) | data3;
Nathan
quelle
Wenn Sie dies tun: (array[i] << 24) | ((array[i + 1] & 0xff) << 16) | ((array[i + 2] & 0xff) << 8) | (array[i + 3] & 0xff)(dh keine Konvertierung in die shorterste), ist die Leistung im Vergleich zur ByteBufferLösung gleich gut . Ich denke, es könnte als allgemeines Muster optimiert werden.
John16384
0

Konvertieren eines 4-Byte-Arrays in eine Ganzzahl:

//Explictly declaring anInt=-4, byte-by-byte
byte[] anInt = {(byte)0xff,(byte)0xff,(byte)0xff,(byte)0xfc}; // Equals -4
//And now you have a 4-byte array with an integer equaling -4...
//Converting back to integer from 4-bytes...
result = (int) ( anInt[0]<<24 | ( (anInt[1]<<24)>>>8 ) | ( (anInt[2]<<24)>>>16) | ( (anInt[3]<<24)>>>24) );
mark_infinite
quelle