Преобразование байтового массива в целое число в Java и наоборот

139

Я хочу сохранить некоторые данные в байтовые массивы в Java. В основном только числа, которые могут занимать до 2 байтов на число.

Я хотел бы знать, как я могу преобразовать целое число в 2-байтовый массив байтов и наоборот. Я нашел множество решений, которые гуглили, но большинство из них не объясняют, что происходит в коде. Я много чего не понимаю, поэтому я был бы признателен за базовое объяснение.

Крис
источник
4
Сколько же вы понимаете , о битном смещении? Похоже, вопрос действительно в том, «что делает сдвиг битов», больше, чем в преобразовании в байтовые массивы, на самом деле - если вы действительно хотите понять, как будет работать преобразование.
Джон Скит
1
(Просто чтобы уточнить, я согласен с любым из этих вопросов, но стоит пояснить, на какой вопрос вы действительно хотите получить ответ. Вы, вероятно, получите ответ, который более полезен для вас таким образом.)
Джон Скит,
Хорошо, я понял вашу точку зрения! Спасибо за замечание. Я знаю, что такое сдвиг битов, я просто еще не понял, для чего он используется при преобразовании байтовых массивов.
Крис
3
@prekageo и Джефф Меркадо Спасибо за ваши два ответа. Prekageo дал хорошее объяснение того, как это делается, хорошая ссылка! Это делает это намного понятнее для меня. И решение Джеффа Меркадоса решило проблему, с которой я столкнулся.
Крис

Ответы:

230

Используйте классы найденные в java.nioпространстве имен, в частности, ByteBuffer. Он может сделать всю работу за вас.

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
Это слишком дорого, если байтовый массив содержит только 1 или 2 целых числа? Не уверен насчет стоимости строительства ByteBuffer.
Мяу Кот 2012
Как часто вы работаете с двоичными данными в 2-4-байтовых чанках? В самом деле? Разумная реализация будет либо работать с ней в кусках BUFSIZ (обычно 4 КБ), либо использовать другие библиотеки ввода-вывода, которые скрывают эту деталь. Внутри фреймворка есть целая библиотека, которая поможет вам работать с буферами данных. Вы оказываете плохую услугу себе и другим сопровождающим вашего кода, когда вы выполняете обычные операции без уважительной причины (будь то перфоманс или другая критическая операция). Эти буферы являются просто обертками, которые работают с массивами, не более того.
Джефф Меркадо
Почему вы можете создать экземпляр абстрактного класса?
1
@JaveneCPPMcGowan В этом ответе нет прямой инстанции. Если вы имеете в виду фабричные методы wrapи allocate, они не возвращают экземпляр абстрактного класса ByteBuffer.
Марко Топольник
Не решение для 3-байтового шага. Мы можем получить Char, Short, Int. Полагаю, я мог бы добавить 4 байта и каждый раз сбрасывать 4-й, но я бы предпочел этого не делать.
Джон
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 );
}

При упаковке подписанных байтов в int каждый байт должен быть замаскирован, потому что он расширен до 32 бит (а не до нуля) из-за правила арифметического продвижения (описано в JLS, Conversions and Promotions).

Есть интересная головоломка, связанная с этим, описанная в Java Puzzlers («Большое наслаждение каждым байтом») Джошуа Блоха и Нила Гафтера. При сравнении значения байта со значением типа int байт расширяется до значения типа int, а затем это значение сравнивается с другим значением типа int.

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

Обратите внимание, что все числовые типы подписаны в Java, за исключением того, что char является 16-разрядным целочисленным типом без знака.

Ярек Пшигодзки
источник
Я думаю, что & 0xFFони не нужны.
Ори Поповски
11
@LeifEricson Я считаю, что & 0xFFs необходимы, так как он говорит JVM преобразовать подписанный байт в целое число только с этими установленными битами. В противном случае байт -1 (0xFF) превратится в int -1 (0xFFFFFFFF). Я могу ошибаться, и даже если это так, это не больно и проясняет ситуацию.
coderforlife
4
& 0xFF действительно обязательно. 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 Не совсем. Перед выполнения и операций на byteс 0xFF ( int), JVM будет кастовал byteк intс 1 расширенными или 0 расширенными согласовывая ведущим битый первым. В Java нет неподписанного байта , bytes всегда подписаны.
Nier
2
Когда анализируете int из байтового массива, обратите внимание на размер байтового массива, если он больше 4 байтов, согласно документу ByteBuffer.getInt():, Reads the next four bytes at this buffer's current positionбудут проанализированы только первые 4 байта, что не должно быть тем, что вы хотите.
Бен
57

Вы также можете использовать BigInteger для байтов переменной длины. Вы можете преобразовать его в long, int или short, в зависимости от ваших потребностей.

new BigInteger(bytes).intValue();

или для обозначения полярности:

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

Чтобы вернуть байты просто:

new BigInteger(bytes).toByteArray()
Джамел Томс
источник
1
Обратите внимание , что с 1.8, это intValueExactнеintValue
Abhijit Sarkar
5

Базовая реализация будет выглядеть примерно так:

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

Чтобы понять вещи, вы можете прочитать эту статью WP: http://en.wikipedia.org/wiki/Endianness

Приведенный выше исходный код будет выводиться 34 12 78 56 bc 9a. Первые 2 байта ( 34 12) представляют первое целое число и т. Д. Приведенный выше исходный код кодирует целые числа в формате с прямым порядком байтов.

prekageo
источник
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

Кто-то с требованием, где он должен читать из битов, скажем, вы должны читать только из 3 битов, но вам нужно целое число со знаком, затем используйте следующее:

data is of type: java.util.BitSet

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

Магическое число 3может быть заменено количеством используемых вами битов (не байтов) .

Vishrant
источник
0

Как часто гуава имеет то, что вам нужно.

Чтобы перейти от байтового массива к int:, здесьInts.fromBytesArray документ

Чтобы перейти от int к байтовому массиву:, здесьInts.toByteArray документ

Жереми
источник
-7

я думаю, что это лучший режим, чтобы привести к Int

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

первый преобразованный байт в строку

comb=B+"";

Следующим шагом является преобразование в Int

out= Integer.parseInt(comb);

но байт находится в ярости от -128 до 127 для этого, я думаю, лучше использовать ярость от 0 до 255, и вам нужно только сделать это:

out=out+256;
Анхель Сальвадор Айала Очоа
источник
Это не верно. Рассмотрим байт 0x01. Ваш метод выдаст 129, что неверно. 0x01 должно вывести целое число 1. Вы должны добавить только 128, если целое число, которое вы получаете от parseInt, является отрицательным.
disclosr
Я имел в виду, что вы должны добавить 256, а не 128. Не удалось отредактировать его впоследствии.
disclosr
изменил пост, чтобы добавить 256, поскольку это может быть полезно для других!
apmartin1991
Это приводит к большому количеству приведения и созданию новых объектов (подумайте, делая это для циклов), которые могут ухудшить производительность, пожалуйста, проверьте метод Integer.toString () для подсказок о том, как анализировать числа.
Маркос Васконселос
Кроме того, при публикации кода в stackoverflow смысл состоит в том, чтобы опубликовать код, который легко имеет смысл. Код, который легко понять, должен иметь понятные идентификаторы. А на stackoverflow, понятно, обязательно означает по-английски .
Майк Накис