Converter uma matriz de bytes em número inteiro em Java e vice-versa


139

Eu quero armazenar alguns dados em matrizes de bytes em Java. Basicamente, apenas números que podem levar até 2 bytes por número.

Gostaria de saber como converter um número inteiro em uma matriz de 2 bytes e vice-versa. Encontrei muitas soluções pesquisando no Google, mas a maioria delas não explica o que acontece no código. Há muitas coisas que eu realmente não entendo, então eu apreciaria uma explicação básica.


4
Quanto é que você entende sobre bit mudança? Parece que a pergunta é realmente "o que a mudança de bits faz" mais do que sobre a conversão em matrizes de bytes, na verdade - se você realmente deseja entender como a conversão funcionaria.
— Jon Skeet

1
(Só para esclarecer, eu estou bem com qualquer uma das perguntas, mas vale a pena esclarecer qual pergunta você realmente deseja que seja respondida. É provável que você obtenha uma resposta que seja mais útil para você dessa maneira.)
— Jon Skeet,

Ok, eu entendi o seu ponto! Obrigado pela observação. Eu sei que mudança de bit eu ainda não entendi para que é usado na conversão de matrizes de bytes.
— Chris

3
@prekageo e Jeff Mercado Obrigado por suas duas respostas. O prekageo deu uma boa explicação de como isso é feito, bom link! Isso torna muito mais claro para mim. E a solução Jeff Mercados resolveu o problema que eu tinha.
— Chris

Respostas:


230

Use as classes encontradas no java.nioespaço para nome, em particular o ByteBuffer. Pode fazer todo o trabalho para você.

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
É muito caro se a matriz de bytes contiver apenas 1 ou 2 números inteiros? Não tenho certeza sobre o custo de construção a ByteBuffer.
— Meow Cat 2012

Com que frequência você trabalha com dados binários em pedaços de 2 a 4 bytes? Realmente? Uma implementação sã funcionaria com ela em pedaços BUFSIZ (normalmente 4kb) ou usaria outras bibliotecas de E / S que ocultam esses detalhes. Há uma biblioteca inteira dentro da estrutura dedicada a ajudá-lo a trabalhar em buffers de dados. Você faz um desserviço a si mesmo e a outros mantenedores do seu código ao implementar operações comuns sem uma boa razão (seja perf ou outra operação crítica). Esses buffers são meramente invólucros que operam em matrizes, nada mais.
— Jeff Mercado

Como você pode instanciar uma classe abstrata?

1
@JaveneCPPMcGowan Não há instanciação direta presente nesta resposta. Se você quer dizer os métodos factory wrape allocate, eles não retornam uma instância da classe abstrata ByteBuffer.
— Marko Topolnik

Não é uma solução para passos de 3 bytes. Podemos obter Char, Short, Int. Suponho que eu poderia preencher até 4 bytes e descartar o quarto de cada vez, mas prefiro não.
— 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 );
}

Ao compactar bytes assinados em um int, cada byte precisa ser mascarado porque é estendido para 32 bits (em vez de estendido para zero) devido à regra aritmética de promoção (descrita em JLS, Conversões e Promoções).

Há um quebra-cabeça interessante relacionado a isso descrito em Java Puzzlers ("Um grande prazer em todos os bytes") de Joshua Bloch e Neal Gafter. Ao comparar um valor de byte com um valor int, o byte é estendido para um int e, em seguida, esse valor é comparado ao outro int

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

Observe que todos os tipos numéricos são assinados em Java, com exceção do char sendo um tipo inteiro não assinado de 16 bits.


Eu acho que os & 0xFFsão desnecessários.
— Ori Popowski

11
@ LeifEricson Eu acredito que os & 0xFFsão necessários, pois informa a JVM para converter o byte assinado em um número inteiro com apenas esses bits definidos. Caso contrário, o byte -1 (0xFF) se transformará no int -1 (0xFFFFFFFF). Eu posso estar errado e, mesmo estando, não dói e torna as coisas mais claras.
— Coderforlife

4
& 0xFF é realmente obrigatório. 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 Na verdade não. Antes de executar e operar bytecom 0xFF ( int), a JVM converterá o bytepara intcom 1 estendido ou 0 estendido de acordo com o bit inicial primeiro. Não há byte não assinado em Java, bytesempre são assinados.
— Nier 24/03

2
Quando int parse da matriz de bytes, prestar atenção ao tamanho da matriz de bytes, se é maior do que 4 bytes, de acordo com o doc de ByteBuffer.getInt(): Reads the next four bytes at this buffer's current positionapenas os primeiros 4 bytes será analisado, que não deve ser o que quiser.
— Bin

56

Você também pode usar o BigInteger para bytes de comprimento variável. Você pode convertê-lo para comprido, int ou curto, conforme a sua necessidade.

new BigInteger(bytes).intValue();

ou para denotar polaridade:

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

Para recuperar bytes apenas:

new BigInteger(bytes).toByteArray()

1
Note-se que desde 1.8, é intValueExact, nãointValue
— Abhijit Sarkar

5

Uma implementação básica seria algo como isto:

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 as coisas, você pode ler este artigo do WP: http://en.wikipedia.org/wiki/Endianness

O código fonte acima será exibido 34 12 78 56 bc 9a. Os primeiros 2 bytes ( 34 12) representam o primeiro número inteiro etc. O código fonte acima codifica números inteiros no 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

Alguém com um requisito onde eles precisam ler de bits, digamos que você precise ler apenas de 3 bits, mas você precisa de um número inteiro assinado e use o seguinte:

data is of type: java.util.BitSet

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

O número mágico 3pode ser substituído pelo número de bits (não bytes) que você está usando.


0

Com freqüência, goiaba tem o que você precisa.

Para passar da matriz de bytes para int Ints.fromBytesArray:, doc aqui

Para ir da matriz de int para byte:, Ints.toByteArraydoc aqui


-7

Eu acho que este é o melhor modo de transmitir para int

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

primeiro byte de conversão para String

comb=B+"";

próximo passo é converter para um int

out= Integer.parseInt(comb);

mas byte está na faixa de -128 a 127 para este reasone, acho melhor usar a faixa de 0 a 255 e você só precisa fazer isso:

out=out+256;

Isto está errado. Considere o byte 0x01. Seu método produzirá 129, o que está errado. 0x01 deve gerar o número inteiro 1. Você só deve adicionar 128 se o número inteiro obtido de parseInt for negativo.
— disklosr

Eu quis dizer que você deve adicionar 256 e não 128. Não foi possível editá-lo depois.
— disklosr

post alterado para adicionar os 256, pois pode ser útil para outras pessoas!
— Apmartin1991

Isso faz muita conversão e cria novos objetos (pense em fazer isso para loops) que podem prejudicar o desempenho; verifique o método Integer.toString () para obter dicas sobre como analisar números.
— Marcos Vasconcelos

Além disso, ao postar código no stackoverflow, o objetivo é postar código que faça sentido com facilidade. Código que facilmente faz sentido deve ter identificadores compreensíveis . E no stackoverflow, compreensível significa necessariamente em inglês .
— Mike Nakis
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.