Перетворити масив байтів у цілі числа в 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.
Meow Cat 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 завжди підписані.
Нір

2
Під час розбору int з байтового масиву зверніть увагу на розмір байтового масиву, якщо він перевищує 4 байти, відповідно до doc of ByteBuffer.getInt():, Reads the next four bytes at this buffer's current positionбудуть розібрані лише перші 4 байти, що не повинно бути тим, що ви хочете.
Бін

57

Також ви можете використовувати BigInteger для байтів змінної довжини. Ви можете перетворити його на довгий, інт або короткий, залежно від ваших потреб.

new BigInteger(bytes).intValue();

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

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

Щоб повернути байти просто:

new BigInteger(bytes).toByteArray()

1
Зауважте, що починаючи з 1,8 року, це intValueExactне такintValue
Абхіджіт Саркар,

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) представляють перше ціле число тощо. Вище наведений вихідний код кодує цілі числа у форматі маленького ендіану.


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можна замінити кількістю використовуваних бітів (не байтів) .


0

Як часто у гуави є те, що потрібно.

Щоб перейти з масиву байтів до int:, Ints.fromBytesArraydoc тут

Щоб перейти від int до байтового масиву:, Ints.toByteArraydoc тут


-7

Я думаю, що це найкращий режим для передачі Int

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

перший байт конвертувати в String

comb=B+"";

Наступний крок - перетворення в int

out= Integer.parseInt(comb);

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

out=out+256;

Це неправильно. Розглянемо байт 0x01. Ваш метод виведе 129, що неправильно. 0x01 має вивести ціле число 1. Ви повинні додати 128, лише якщо ціле число, отримане від parseInt, від'ємне.
disklosr

Я мав на увазі, що ви повинні додати 256, а не 128. Не вдалося змінити його згодом.
disklosr

змінено публікацію, щоб додати 256, як це може бути корисно іншим!
apmartin1991

Це робить багато кастингу та створює нові об’єкти (думаю, це робиться для циклів), які можуть погіршити продуктивність, будь ласка, перевірте метод Integer.toString () для підказки про те, як аналізувати числа.
Маркос Васконцелос

Крім того, при розміщенні коду на stackoverflow справа полягає в поштовому коді, який легко має сенс. Код, який легко має сенс, повинен мати зрозумілі ідентифікатори. А на stackoverflow, зрозуміле обов'язково означає англійською .
Майк
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.