Konvertieren Sie ein Byte-Array in Java in eine Ganzzahl und umgekehrt

139

Ich möchte einige Daten in Byte-Arrays in Java speichern. Grundsätzlich nur Zahlen, die bis zu 2 Bytes pro Zahl aufnehmen können.

Ich möchte wissen, wie ich eine Ganzzahl in ein 2 Byte langes Byte-Array konvertieren kann und umgekehrt. Ich habe viele Lösungen gefunden, die googeln, aber die meisten erklären nicht, was im Code passiert. Es gibt viele wechselnde Dinge, die ich nicht wirklich verstehe, daher würde ich mich über eine grundlegende Erklärung freuen.

Chris
quelle
4
Wie viel verstehen Sie über Bitverschiebung? Es hört sich so an, als ob die Frage wirklich "Was macht Bitverschiebung?" Mehr ist als die Konvertierung in Byte-Arrays - wenn Sie wirklich verstehen wollen, wie die Konvertierung funktionieren würde.
Jon Skeet
1
(Nur zur Klarstellung, ich bin mit beiden Fragen einverstanden, aber es lohnt sich klar zu machen, welche Frage Sie wirklich beantworten möchten. Sie werden wahrscheinlich eine Antwort erhalten, die für Sie auf diese Weise nützlicher ist.)
Jon Skeet
Okay, ich habe verstanden! Danke für die Bemerkung. Ich weiß, was Bitverschiebung bedeutet. Ich habe nur noch nicht verstanden, wofür es beim Konvertieren von Byte-Arrays verwendet wird.
Chris
3
@prekageo und Jeff Mercado Danke für deine zwei Antworten. prekageo gab eine gute Erklärung, wie das gemacht wird, netter Link! Das macht es mir viel klarer. Und Jeff Mercados Lösung löste das Problem, das ich hatte.
Chris

Antworten:

229

Verwenden Sie die im java.nioNamespace gefundenen Klassen , insbesondere die ByteBuffer. Es kann die ganze Arbeit für Sie erledigen.

byte[] arr = { 0x00, 0x01 };
ByteBuffer wrapped = ByteBuffer.wrap(arr); // big-endian by default
short num = wrapped.getShort(); // 1

ByteBuffer dbuf = ByteBuffer.allocate(2);
dbuf.putShort(num);
byte[] bytes = dbuf.array(); // { 0, 1 }
Jeff Mercado
quelle
2
Ist es zu teuer, wenn das Byte-Array nur 1 oder 2 Ganzzahlen enthält? Ich bin mir nicht sicher über die Kosten für den Bau eines ByteBuffer.
Meow Cat 2012
Wie oft arbeiten Sie mit Binärdaten in 2-4-Byte-Blöcken? "Ja wirklich?" Eine vernünftige Implementierung würde entweder in BUFSIZ-Blöcken (normalerweise 4 KB) damit funktionieren oder andere E / A-Bibliotheken verwenden, die dieses Detail verbergen. Innerhalb des Frameworks befindet sich eine ganze Bibliothek, die Sie bei der Arbeit an Datenpuffern unterstützt. Sie tun sich selbst und anderen Betreuern Ihres Codes einen schlechten Dienst, wenn Sie allgemeine Vorgänge ohne triftigen Grund implementieren (sei es perf oder ein anderer kritischer Vorgang). Diese Puffer sind lediglich Wrapper, die mit Arrays arbeiten, nicht mehr.
Jeff Mercado
Wie kommt es, dass Sie eine abstrakte Klasse instanziieren können?
1
@JaveneCPPMcGowan Diese Antwort enthält keine direkte Instanziierung. Wenn Sie die Factory-Methoden wrapund meinen allocate, geben sie keine Instanz der abstrakten Klasse zurück ByteBuffer.
Marko Topolnik
Keine Lösung für 3-Byte-Schritte. Wir können bekommen Char, Short, Int. Ich nehme an, ich könnte jedes Mal auf 4 Bytes auffüllen und das 4. verwerfen, aber ich würde es lieber nicht tun.
John
128
byte[] toByteArray(int value) {
     return  ByteBuffer.allocate(4).putInt(value).array();
}

byte[] toByteArray(int value) {
    return new byte[] { 
        (byte)(value >> 24),
        (byte)(value >> 16),
        (byte)(value >> 8),
        (byte)value };
}

int fromByteArray(byte[] bytes) {
     return ByteBuffer.wrap(bytes).getInt();
}
// packing an array of 4 bytes to an int, big endian, minimal parentheses
// operator precedence: <<, &, | 
// when operators of equal precedence (here bitwise OR) appear in the same expression, they are evaluated from left to right
int fromByteArray(byte[] bytes) {
     return bytes[0] << 24 | (bytes[1] & 0xFF) << 16 | (bytes[2] & 0xFF) << 8 | (bytes[3] & 0xFF);
}

// packing an array of 4 bytes to an int, big endian, clean code
int fromByteArray(byte[] bytes) {
     return ((bytes[0] & 0xFF) << 24) | 
            ((bytes[1] & 0xFF) << 16) | 
            ((bytes[2] & 0xFF) << 8 ) | 
            ((bytes[3] & 0xFF) << 0 );
}

Beim Packen von vorzeichenbehafteten Bytes in ein int muss jedes Byte maskiert werden, da es aufgrund der arithmetischen Heraufstufungsregel (beschrieben in JLS, Conversions and Promotions) auf 32 Bit vorzeichenerweitert (anstatt auf null erweitert) ist.

Es gibt ein interessantes Puzzle, das in Java Puzzlers ("Eine große Freude in jedem Byte") von Joshua Bloch und Neal Gafter beschrieben ist. Beim Vergleichen eines Bytewerts mit einem int-Wert wird das Byte vorzeichenerweitert zu einem int und dieser Wert wird dann mit dem anderen int verglichen

byte[] bytes = (…)
if (bytes[0] == 0xFF) {
   // dead code, bytes[0] is in the range [-128,127] and thus never equal to 255
}

Beachten Sie, dass alle numerischen Typen in Java signiert sind, mit Ausnahme von char, einem 16-Bit-Integer-Typ ohne Vorzeichen.

Jarek Przygódzki
quelle
Ich denke die & 0xFFs sind unnötig.
Ori Popowski
11
@LeifEricson Ich glaube, das & 0xFFs ist notwendig, da es die JVM anweist , das vorzeichenbehaftete Byte in eine Ganzzahl umzuwandeln, wobei nur diese Bits gesetzt sind. Andernfalls wird das Byte -1 (0xFF) zum int -1 (0xFFFFFFFF). Ich könnte mich irren und selbst wenn ich es bin, tut es nicht weh und macht die Dinge klarer.
Coderforlife
4
& 0xFF ist in der Tat obligatorisch. byte b = 0; b |= 0x88; System.out.println(Integer.toString(b, 16)); //Output: -78 System.out.println(Integer.toString(b & 0xFF, 16)); //Output: 88
HBN
1
@ptntialunrlsd Eigentlich nicht. Bevor Sie die Durchführung und den Betrieb auf bytemit 0xFF ( int) wird JVM werfen die byteauf intmit 1 verlängert oder 0 erweitert die nach dem führenden Bit zuerst. In Java gibt es kein vorzeichenloses Byte , bytes sind immer signiert.
Nier
2
Achten Sie beim Parsen von int aus dem Byte-Array auf die Größe des Byte-Arrays. Wenn es größer als 4 Byte ist, werden laut Dokument von ByteBuffer.getInt(): Reads the next four bytes at this buffer's current positionnur die ersten 4 Bytes analysiert, was nicht das sein sollte, was Sie wollen.
Bin
56

Sie können BigInteger auch für Bytes variabler Länge verwenden. Sie können es in long, int 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();

Um Bytes zurückzubekommen, einfach:

new BigInteger(bytes).toByteArray()
Jamel Toms
quelle
5

Eine grundlegende Implementierung wäre ungefähr so:

public class Test {
    public static void main(String[] args) {
        int[] input = new int[] { 0x1234, 0x5678, 0x9abc };
        byte[] output = new byte[input.length * 2];

        for (int i = 0, j = 0; i < input.length; i++, j+=2) {
            output[j] = (byte)(input[i] & 0xff);
            output[j+1] = (byte)((input[i] >> 8) & 0xff);
        }

        for (int i = 0; i < output.length; i++)
            System.out.format("%02x\n",output[i]);
    }
}

Um die Dinge zu verstehen, können Sie diesen WP-Artikel lesen: http://en.wikipedia.org/wiki/Endianness

Der obige Quellcode wird ausgegeben 34 12 78 56 bc 9a. Die ersten 2 Bytes ( 34 12) stellen die erste Ganzzahl usw. dar. Der obige Quellcode codiert Ganzzahlen im Little-Endian-Format.

prekageo
quelle
2
/** length should be less than 4 (for int) **/
public long byteToInt(byte[] bytes, int length) {
        int val = 0;
        if(length>4) throw new RuntimeException("Too big to fit in int");
        for (int i = 0; i < length; i++) {
            val=val<<8;
            val=val|(bytes[i] & 0xFF);
        }
        return val;
    }
Dhaval Rami
quelle
0

Jemand mit einer Anforderung, bei der er aus Bits lesen muss, sagen wir, Sie müssen nur aus 3 Bits lesen, aber Sie benötigen eine vorzeichenbehaftete Ganzzahl und verwenden dann Folgendes:

data is of type: java.util.BitSet

new BigInteger(data.toByteArray).intValue() << 32 - 3 >> 32 - 3

Die magische Zahl 3kann durch die Anzahl der von Ihnen verwendeten Bits (nicht Bytes) ersetzt werden.

Vishrant
quelle
0

Wie so oft hat Guave das, was Sie brauchen.

Um vom Byte-Array zu int: zu wechseln Ints.fromBytesArray, dokumentieren Sie hier

Um von int zu Byte Array zu wechseln : Ints.toByteArray, doc hier

Jeremy
quelle
-6

Ich denke, dies ist der beste Modus, um auf int zu übertragen

   public int ByteToint(Byte B){
        String comb;
        int out=0;
        comb=B+"";
        salida= Integer.parseInt(comb);
        out=out+128;
        return out;
    }

erstes Byte in String konvertieren

comb=B+"";

Der nächste Schritt ist das Konvertieren in ein Int

out= Integer.parseInt(comb);

aber Byte ist in Wut von -128 bis 127 für diesen Grund, ich denke, ist besser, Wut 0 bis 255 zu verwenden, und Sie müssen nur dies tun:

out=out+256;
Engel Salvador Ayala Ochoa
quelle
Das ist falsch. Betrachten Sie das Byte 0x01. Ihre Methode gibt 129 aus, was falsch ist. 0x01 sollte die Ganzzahl 1 ausgeben. Sie sollten 128 nur hinzufügen, wenn die Ganzzahl, die Sie von parseInt erhalten, negativ ist.
Disklosr
Ich meinte, Sie sollten 256 nicht 128 hinzufügen. Konnte es danach nicht bearbeiten.
Disklosr
Beitrag geändert, um die 256 hinzuzufügen, da es für andere nützlich sein kann!
Apmartin1991
Dies führt viel Casting durch und erstellt neue Objekte (denken Sie daran, dies für Schleifen zu tun), die die Leistung beeinträchtigen können. Überprüfen Sie die Integer.toString () -Methode auf Hinweise zum Parsen von Zahlen.
Marcos Vasconcelos
Wenn Sie Code im Stackoverflow veröffentlichen, müssen Sie auch Code eingeben, der leicht sinnvoll ist. Code, der leicht Sinn macht, muss verständliche Bezeichner haben. Und beim Stackoverflow bedeutet verständlich unbedingt Englisch .
Mike Nakis