Converti un array di byte in numero intero in Java e viceversa


139

Voglio archiviare alcuni dati in array di byte in Java. Fondamentalmente solo numeri che possono richiedere fino a 2 byte per numero.

Vorrei sapere come posso convertire un numero intero in un array di byte lungo 2 byte e viceversa. Ho trovato molte soluzioni su Google, ma la maggior parte di loro non spiega cosa succede nel codice. Ci sono molte cose mutevoli che non capisco davvero, quindi apprezzerei una spiegazione di base.


4
Quanto si fa a capire su po spostamento? Sembra che la domanda sia davvero "cosa fa lo spostamento dei bit" più della conversione in array di byte, davvero - se davvero vuoi capire come funzionerebbe la conversione.
Jon Skeet,

1
(Giusto per chiarire, sto bene con entrambe le domande, ma vale la pena chiarire a quale domanda vuoi davvero rispondere. Probabilmente otterrai una risposta che ti è più utile in quel modo.)
Jon Skeet

Ok ho capito il tuo punto! Grazie per l'osservazione. So che bit shifting non ho ancora capito a cosa servisse nel convertire array di byte.
Chris,

3
@prekageo e Jeff Mercado Grazie per le tue due risposte. prekageo ha dato una buona spiegazione di come è fatto, bel link! Questo mi rende molto più chiaro. E la soluzione di Jeff Mercados ha risolto il problema che avevo.
Chris,

Risposte:


230

Utilizzare le classi trovate nello java.niospazio dei nomi, in particolare, il ByteBuffer. Può fare tutto il lavoro per te.

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 }

2
È troppo costoso se l'array di byte contiene solo 1 o 2 numeri interi? Non sono sicuro del costo di costruzione a ByteBuffer.
Meow Cat 2012

Con quale frequenza lavori con dati binari in blocchi da 2-4 byte? Veramente? Un'implementazione sana funzionerebbe con essa in blocchi BUFSIZ (in genere 4kb) o userebbe altre librerie IO che nascondono questo dettaglio. C'è un'intera libreria all'interno del framework dedicata ad aiutarti a lavorare sui buffer di dati. Fai un disservizio a te stesso e agli altri manutentori del tuo codice quando esegui operazioni comuni senza una buona ragione (sia esso perf o altre operazioni critiche). Questi buffer sono semplicemente wrapper che operano su array, niente di più.
Jeff Mercado,

Come mai puoi istanziare una classe astratta?

1
@JaveneCPPMcGowan Non esiste un'istanza diretta presente in questa risposta. Se intendi i metodi factory wrape allocatenon restituiscono un'istanza della classe astratta ByteBuffer.
Marko Topolnik,

Non è una soluzione per il passo da 3 byte. Siamo in grado di ottenere Char, Short, Int. Suppongo che potrei passare a 4 byte e scartare il 4 ogni volta, ma preferirei non farlo.
Giovanni

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 );
}

Quando si comprimono byte firmati in un int, ogni byte deve essere mascherato perché è esteso a segno a 32 bit (anziché a zero) a causa della regola di promozione aritmetica (descritta in JLS, Conversioni e Promozioni).

C'è un puzzle interessante correlato a questo descritto in Java Puzzlers ("Una grande delizia in ogni byte") di Joshua Bloch e Neal Gafter. Quando si confronta un valore di byte con un valore int, il byte viene esteso a un int e quindi questo valore viene confrontato con l'altro int

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

Si noti che tutti i tipi numerici sono firmati in Java con l'eccezione che char è un tipo intero senza segno a 16 bit.


Penso che le & 0xFFs non siano necessarie.
Ori Popowski

11
@LeifEricson Credo che & 0xFFsiano necessari in quanto indica alla JVM di convertire il byte con segno in un numero intero con solo quei bit impostati. Altrimenti il ​​byte -1 (0xFF) si trasformerà in int -1 (0xFFFFFFFF). Potrei sbagliarmi e anche se lo sono non fa male e rende le cose più chiare.
coderforlife,

4
& 0xFF è davvero obbligatorio. 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 In realtà non. Prima di eseguire e operazione su bytecon 0xFF ( int), JVM lancerà la bytea intcon 1 esteso o 0 esteso secondo il leader po 'prima. Non ci sono byte senza segno in Java, bytesono sempre firmati.
Nier

2
Quando int parse da array di byte, prestare attenzione alla dimensione della matrice di byte, se è maggiore di 4 byte, secondo il doc di ByteBuffer.getInt(): Reads the next four bytes at this buffer's current position, solo verrà analizzato i primi 4 byte, che non dovrebbe essere quello che vuoi.
Bin

57

Puoi anche usare BigInteger per byte di lunghezza variabile. Puoi convertirlo in long, int o short, a seconda delle tue esigenze.

new BigInteger(bytes).intValue();

o per indicare la polarità:

new BigInteger(1, bytes).intValue();

Per recuperare byte solo:

new BigInteger(bytes).toByteArray()

1
Si noti che dal 1.8, intValueExactnon èintValue
Abhijit Sarkar il

5

Un'implementazione di base sarebbe qualcosa del genere:

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]);
    }
}

Per capire le cose puoi leggere questo articolo del WP: http://en.wikipedia.org/wiki/Endianness

Verrà emesso il codice sorgente sopra 34 12 78 56 bc 9a. I primi 2 byte ( 34 12) rappresentano il primo numero intero, ecc. Il codice sorgente sopra codifica numeri interi in formato little endian.


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;
    }

0

Qualcuno con un requisito in cui devono leggere da bit, supponiamo che tu debba leggere da soli 3 bit ma hai bisogno di un intero con segno, quindi usa quanto segue:

data is of type: java.util.BitSet

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

Il numero magico 3può essere sostituito con il numero di bit (non byte) che si sta utilizzando.


0

Come spesso, la guava ha ciò di cui hai bisogno.

Per passare dall'array di byte a int Ints.fromBytesArray:, doc qui

Per passare da int a byte array Ints.toByteArray:, doc qui


-7

penso che questa sia la modalità migliore per trasmettere a int

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

primo byte convertito in stringa

comb=B+"";

il prossimo passo è convertito in un int

out= Integer.parseInt(comb);

ma byte ha una rabbia da -128 a 127 per questo motivo, penso che sia meglio usare la rabbia da 0 a 255 e devi solo fare questo:

out=out+256;

Questo è sbagliato. Considera il byte 0x01. Il tuo metodo produrrà 129 che è sbagliato. 0x01 dovrebbe generare l'intero 1. Dovresti aggiungere 128 solo se il numero intero ottenuto da parseInt è negativo.
disklosr

Volevo dire che dovresti aggiungere 256 e non 128. Non puoi modificarlo in seguito.
disklosr

post modificato per aggiungere il 256 in quanto potrebbe essere utile ad altri!
apmartin1991,

Questo fa un sacco di casting e crea nuovi oggetti (pensa di farlo per i loop) che possono degradare le prestazioni, controlla il metodo Integer.toString () per suggerimenti su come analizzare i numeri.
Marcos Vasconcelos,

inoltre, quando si pubblica codice su StackOverflow, il punto è pubblicare codice che abbia facilmente senso. Il codice che ha facilmente senso deve avere identificatori comprensibili . E su StackOverflow, comprensibile significa necessariamente in inglese .
Mike Nakis,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.