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.

Chris
fonte
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 }
Jeff Mercado
fonte
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.

Jarek Przygódzki
fonte
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()
Jamel Toms
fonte
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.

prekageo
fonte
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
fonte
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.

Vishrant
fonte
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

jeremie
fonte
-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;
Angel Salvador Ayala Ochoa
fonte
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