Як ініціалізувати байтовий масив у Java?


138

Мені доводиться зберігати деякі постійні значення (UUID) у формі байтового масиву в java, і мені цікаво, який найкращий спосіб ініціалізувати ці статичні масиви. Це я зараз роблю, але відчуваю, що повинен бути кращий шлях.

private static final byte[] CDRIVES = new byte[] { (byte)0xe0, 0x4f, (byte)0xd0,
    0x20, (byte)0xea, 0x3a, 0x69, 0x10, (byte)0xa2, (byte)0xd8, 0x08, 0x00, 0x2b,
    0x30, 0x30, (byte)0x9d };
private static final byte[] CMYDOCS = new byte[] { (byte)0xba, (byte)0x8a, 0x0d,
    0x45, 0x25, (byte)0xad, (byte)0xd0, 0x11, (byte)0x98, (byte)0xa8, 0x08, 0x00,
    0x36, 0x1b, 0x11, 0x03 };
private static final byte[] IEFRAME = new byte[] { (byte)0x80, 0x53, 0x1c,
    (byte)0x87, (byte)0xa0, 0x42, 0x69, 0x10, (byte)0xa2, (byte)0xea, 0x08,
    0x00, 0x2b, 0x30, 0x30, (byte)0x9d };
...
and so on

Чи можу я використати щось, що може бути менш ефективним, але виглядатиме чистіше? наприклад:

private static final byte[] CDRIVES =
    new byte[] { "0xe04fd020ea3a6910a2d808002b30309d" };

Відповіді:


111

Використовуючи функцію, перетворюючи шістнадцяткову рядок byte[], ви могли б зробити

byte[] CDRIVES = hexStringToByteArray("e04fd020ea3a6910a2d808002b30309d");

Я б запропонував вам скористатися функцією, визначеною Дейвом L, в Перетворити рядкове представлення шістнадцяткового дампа в байтовий масив за допомогою Java?

Я вставляю його тут для максимальної читабельності:

public static byte[] hexStringToByteArray(String s) {
    int len = s.length();
    byte[] data = new byte[len / 2];
    for (int i = 0; i < len; i += 2) {
        data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                             + Character.digit(s.charAt(i+1), 16));
    }
    return data;
}

Якщо дозволити CDRIVES staticі final, зниження продуктивності не має значення.


78
byte[] myvar = "Any String you want".getBytes();

Для створення будь-якого символу можна уникати рядкових літералів:

byte[] CDRIVES = "\u00e0\u004f\u00d0\u0020\u00ea\u003a\u0069\u0010\u00a2\u00d8\u0008\u0000\u002b\u0030\u0030\u009d".getBytes();

50
Хіба це не перетворює рядок "0000"на {0x30,0x30,0x30,0x30}(ASCII), а не на {0x00,0x00,0x00,0x00}(двійковий), як цього хоче плакат?
jww

5
Подивіться назву питання. Потім погляньте на цю відповідь. А тепер скажіть, що в цьому поганого? Це може не вирішити конкретне питання афіші, але воно, безумовно, вирішило моє. Мені потрібно було перетворити рядок у байтовий масив, щоб використати в якості джерела генератора псевдовипадкових чисел, і це спрацювало як шарм.
e18r

@ e18r Це генерує байти, так, але ви не знаєте, від чого це залежить від типової таблиці. Принаймні використовуйте .getBytes (бажане кодування).
квант

@petmez тупе запитання: у JAVA щось подібне до "" .getBytes (UTF_8)); (getBytes на порожній рядку) безпечну річ? це "законно"? Або я можу просто зробити: = новий байт [0]; ?
Роберт Ахман

1
@RobertAchmann "" .getbytes ("UTF-8") повинен повертати порожній масив і є абсолютно законним.
Jazzepi

33

У Java 6 є метод, що робить саме те, що ви хочете:

private static final byte[] CDRIVES = javax.xml.bind.DatatypeConverter.parseHexBinary("e04fd020ea3a6910a2d808002b30309d")

Можна також скористатися Google Guava :

import com.google.common.io.BaseEncoding;
private static final byte[] CDRIVES = BaseEncoding.base16().lowerCase().decode("E04FD020ea3a6910a2d808002b30309d".toLowerCase());

Метод Guava є надмірним, коли ви використовуєте невеликі масиви. Але у Guava також є версії, які можуть аналізувати вхідні потоки. Це приємна особливість при роботі з великими шістнадцятковими входами.


Приклад Guava не так добре працює, як написано - це має бути, base16().lowerCase().decode(...)якщо у вас є шістнадцяткові цифри. docs.guava-libraries.googlecode.com/git/javadoc/com/google/…
Peter DeGlopper

@PeterDeGlopper Хороший висновок, я оновив відповідь, тому код тепер обробляє рядки як із нижнього, так і з верхнього регістру.
stefan.schwetschke

1
javax.xml.bindбуло сумно видалено на Java 9.
randomdude999

7

Клас Java UUID можна використовувати для зберігання цих значень замість байтових масивів:

UUID

public UUID(long mostSigBits,
            long leastSigBits)

Конструює новий UUID з використанням вказаних даних. mostSigBits використовується для найбільш значущих 64 біт UUID, а najmanjeSigBits стає найменш значущим 64 біта UUID.


2

Що стосується чистого процесу, ви можете використовувати об'єкт ByteArrayOutputStream ...

ByteArrayOutputStream bObj = new ByteArrayOutputStream();
bObj.reset();

// записуйте всі значення в bObj по одному за допомогою

bObj.write(byte value)

// коли закінчите, ви можете отримати байт [] за допомогою

CDRIVES = bObj.toByteArray();

// чим ви можете повторити подібний процес для CMYDOCS та IEFRAME,

ПРИМІТКА Це не ефективне рішення, якщо у вас дійсно малий масив.


2

Рішення без бібліотек, повернена динамічна довжина, непідписана ціла інтерпретація (не два доповнення)

    public static byte[] numToBytes(int num){
    if(num == 0){
        return new byte[]{};
    }else if(num < 256){
        return new byte[]{ (byte)(num) };
    }else if(num < 65536){
        return new byte[]{ (byte)(num >>> 8),(byte)num };
    }else if(num < 16777216){
        return new byte[]{ (byte)(num >>> 16),(byte)(num >>> 8),(byte)num };
    }else{ // up to 2,147,483,647
        return new byte[]{ (byte)(num >>> 24),(byte)(num >>> 16),(byte)(num >>> 8),(byte)num };
    }
}

1

Моїм кращим варіантом у цій обставині є використання org.apache.commons.codec.binary.Hexкорисних API для перетворення між Stringшістнадцятковим та бінарним. Наприклад:

  1. Hex.decodeHex(char[] data)який кидає a, DecoderExceptionякщо в масиві є не шістнадцяткові символи, або якщо є непарна кількість символів.

  2. Hex.encodeHex(byte[] data)є аналогом вищезазначеного методу декодування і виплющує char[].

  3. Hex.encodeHexString(byte[] data)який перетворює назад з byteмасиву в a String.

Використання: Hex.decodeHex("dd645a2564cbe648c8336d2be5eafaa6".toCharArray())


1

Ви можете використовувати цю функцію утиліти:

public static byte[] fromHexString(String src) {
    byte[] biBytes = new BigInteger("10" + src.replaceAll("\\s", ""), 16).toByteArray();
    return Arrays.copyOfRange(biBytes, 1, biBytes.length);
}

На відміну від варіантів Denys Séguret та stefan.schwetschke, він дозволяє вставляти розділові символи (пробіли, вкладки тощо) у рядок введення, роблячи його більш читабельним.

Приклад використання:

private static final byte[] CDRIVES
    = fromHexString("e0 4f d0 20 ea 3a 69 10 a2 d8 08 00 2b 30 30 9d");
private static final byte[] CMYDOCS
    = fromHexString("BA8A0D4525ADD01198A80800361B1103");
private static final byte[] IEFRAME
    = fromHexString("80531c87 a0426910 a2ea0800 2b30309d");

1

Найменший внутрішній тип, який під час компіляції можна призначити шістнадцятковими числами, є char , як

private static final char[] CDRIVES_char = new char[] {0xe0, 0xf4, ...};

Щоб мати еквівалентний байтовий масив, можна перетворити конверсії як

public static byte[] charToByteArray(char[] x)
{
    final byte[] res = new byte[x.length];
    for (int i = 0; i < x.length; i++)
    {
        res[i] = (byte) x[i];
    }
    return res;
}

public static byte[][] charToByteArray(char[][] x)
{
    final byte[][] res = new byte[x.length][];
    for (int i = 0; i < x.length; i++)
    {
        res[i] = charToByteArray(x[i]);
    }
    return res;
}

-2
private static final int[] CDRIVES = new int[] {0xe0, 0xf4, ...};

і після доступу перетворити в байт.

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