Convierta una matriz de bytes a entero en Java y viceversa

139

Quiero almacenar algunos datos en conjuntos de bytes en Java. Básicamente solo números que pueden tomar hasta 2 Bytes por número.

Me gustaría saber cómo puedo convertir un número entero en una matriz de bytes de 2 bytes de largo y viceversa. Encontré muchas soluciones en Google, pero la mayoría de ellas no explican qué sucede en el código. Hay muchas cosas cambiantes que realmente no entiendo, así que agradecería una explicación básica.

Chris
fuente
44
¿Cuánto hace que usted entiende sobre desplazamiento de bits? Parece que la pregunta es realmente "qué hace el desplazamiento de bits" más que acerca de la conversión a matrices de bytes, realmente, si realmente quieres entender cómo funcionaría la conversión.
Jon Skeet
1
(Solo para aclarar, estoy de acuerdo con cualquiera de las preguntas, pero vale la pena dejar en claro qué pregunta realmente desea que se responda. Es probable que obtenga una respuesta que le sea más útil de esa manera.)
Jon Skeet
Está bien, tengo tu punto! Gracias por el comentario. Sé qué cambio de bits simplemente no entiendo para qué se utiliza en la conversión de matrices de bytes todavía.
Chris
3
@prekageo y Jeff Mercado Gracias por sus dos respuestas. prekageo dio una buena explicación de cómo se hace esto, ¡buen enlace! Eso lo hace mucho más claro para mí. Y la solución de Jeff Mercados resolvió el problema que tenía.
Chris

Respuestas:

230

Utilice las clases que se encuentran en el java.nioespacio de nombres, en particular, el ByteBuffer. Puede hacer todo el trabajo por ti.

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
fuente
2
¿Es demasiado costoso si la matriz de bytes contiene solo 1 o 2 enteros? No estoy seguro sobre el costo de construcción a ByteBuffer.
Meow Cat 2012
¿Con qué frecuencia trabaja con datos binarios en fragmentos de 2-4 bytes? De Verdad? Una implementación sensata funcionaría con ella en fragmentos BUFSIZ (normalmente 4kb) o usaría otras bibliotecas de E / S que ocultaran este detalle. Hay una biblioteca completa dentro del marco que está dedicada a ayudarlo a trabajar en buffers de datos. Se perjudica a usted mismo y a otros mantenedores de su código cuando implementa operaciones comunes sin una buena razón (ya sea una operación u otra operación crítica). Estos búferes son simplemente envoltorios que funcionan en matrices, nada más.
Jeff Mercado
¿Cómo es que puedes instanciar una clase abstracta?
1
@JaveneCPPMcGowan No hay una instanciación directa presente en esta respuesta. Si te refieres a los métodos de fábrica wrapy allocate, no devuelven una instancia de la clase abstracta ByteBuffer.
Marko Topolnik
No es una solución para zancadas de 3 bytes. Podemos obtener Char, Short, Int. Supongo que podría rellenar a 4 bytes y descartar el cuarto cada vez, pero preferiría que no.
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 );
}

Cuando se empaquetan bytes firmados en un int, cada byte debe enmascararse porque tiene un signo extendido a 32 bits (en lugar de cero extendido) debido a la regla de promoción aritmética (descrita en JLS, Conversiones y Promociones).

Hay un rompecabezas interesante relacionado con esto descrito en Java Puzzlers ("Una gran delicia en cada byte") de Joshua Bloch y Neal Gafter. Al comparar un valor de byte con un valor int, el byte se amplía con signo a un int y luego este valor se compara con el otro int

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

Tenga en cuenta que todos los tipos numéricos están firmados en Java, con la excepción de que char es un tipo entero sin signo de 16 bits.

Jarek Przygódzki
fuente
Creo que los & 0xFFs son innecesarios.
Ori Popowski
11
@LeifEricson Creo que los & 0xFFs son necesarios ya que le dice a la JVM que convierta el byte firmado en un entero con solo esos bits establecidos. De lo contrario, el byte -1 (0xFF) se convertirá en int -1 (0xFFFFFFFF). Podría estar equivocado e incluso si lo estoy, no duele y aclara las cosas.
coderforlife
44
& 0xFF es obligatorio de hecho. 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 En realidad no. Antes de la realización y operación en bytela 0xFF ( int), JVM emitir el byteque intcon 1 extendida o 0 extendido según el líder de la primera bits. No hay byte sin firmar en Java, los bytes siempre están firmados.
Nier
2
Cuando analice int desde la matriz de bytes, preste atención al tamaño de la matriz de bytes, si es mayor que 4 bytes, de acuerdo con el documento de ByteBuffer.getInt():, Reads the next four bytes at this buffer's current positionsolo se analizarán los primeros 4 bytes, que no deberían ser lo que desea.
Bin
57

También puede usar BigInteger para bytes de longitud variable. Puede convertirlo a largo, int o corto, lo que se ajuste a sus necesidades.

new BigInteger(bytes).intValue();

o para denotar polaridad:

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

Para recuperar bytes solo:

new BigInteger(bytes).toByteArray()
Jamel Toms
fuente
1
Tenga en cuenta que desde 1.8, que es intValueExact, nointValue
Abhijit Sarkar
5

Una implementación básica sería algo como esto:

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

Para entender las cosas, puede leer este artículo de WP: http://en.wikipedia.org/wiki/Endianness

Se mostrará el código fuente anterior 34 12 78 56 bc 9a. Los primeros 2 bytes ( 34 12) representan el primer entero, etc. El código fuente anterior codifica enteros en formato little endian.

prekageo
fuente
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
fuente
0

Alguien con un requisito en el que tiene que leer desde bits, digamos que tiene que leer desde solo 3 bits pero necesita un entero con signo y luego use lo siguiente:

data is of type: java.util.BitSet

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

El número mágico 3se puede reemplazar con el número de bits (no bytes) que está utilizando.

Vishrant
fuente
0

Como a menudo, la guayaba tiene lo que necesitas.

Para ir de una matriz de bytes a int: Ints.fromBytesArraydoc aquí

Para ir de la matriz de bytes a int: Ints.toByteArraydoc aquí

jeremie
fuente
-7

Creo que este es el mejor modo para enviar a int

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

primer byte convertido a String

comb=B+"";

el siguiente paso es convertir a un int

out= Integer.parseInt(comb);

pero el byte está en la rabia de -128 a 127 para esta razón, creo que es mejor usar la rabia de 0 a 255 y solo necesitas hacer esto:

out=out+256;
Angel Salvador Ayala Ochoa
fuente
Esto está mal. Considere el byte 0x01. Su método generará 129 que está mal. 0x01 debería generar el entero 1. Solo debe agregar 128 si el entero que obtiene de parseInt es negativo.
disklosr
Quise decir que deberías agregar 256, no 128. No pude editarlo después.
disklosr
¡Se cambió la publicación para agregar 256, ya que puede ser útil para otros!
apmartin1991
Esto realiza una gran cantidad de conversión y crea nuevos objetos (piense en hacer esto para bucles) que pueden degradar el rendimiento, consulte el método Integer.toString () para obtener sugerencias sobre cómo analizar los números.
Marcos Vasconcelos
Además, cuando se publica código en stackoverflow, el punto es publicar código que tenga sentido fácilmente. El código que tiene sentido fácilmente debe tener identificadores comprensibles . Y en stackoverflow, entendible significa necesariamente en inglés .
Mike Nakis