Как инициализировать байтовый массив в Java?

146

Мне нужно сохранить некоторые постоянные значения (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" };
дрочит
источник

Ответы:

117

Используя функцию преобразования шестнадцатеричной строки в 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, падение производительности не имеет значения.

Дени Сегюре
источник
79
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();
петмез
источник
51
Разве это не превращает строку "0000"в {0x30,0x30,0x30,0x30}(ASCII), а не в {0x00,0x00,0x00,0x00}(двоичный), как того требует плакат?
jww
5
Посмотрите на название вопроса. Тогда посмотрите на этот ответ. А теперь скажи мне, что в этом плохого? Возможно, это не решило конкретной проблемы плаката, но определенно решило мою. Мне нужно было преобразовать строку в массив байтов для использования в качестве начального числа для генератора псевдослучайных чисел, и это сработало как шарм.
e18r
@ e18r Он генерирует байты, да, но вы не знаете, какие именно, поскольку это зависит от кодировки по умолчанию. По крайней мере, используйте .getBytes (желаемоеEncoding).
квант
@petmez тупой вопрос: в JAVA это что-то вроде "" .getBytes (UTF_8)); (getBytes на пустой строке) что делать? это «законно»? Или я могу просто сделать: = новый байт [0]; ?
Роберт Ахманн 01
1
@RobertAchmann "" .getbytes ("UTF-8") должен возвращать пустой массив и совершенно допустим.
Jazzepi
34

В 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 есть также версии, которые могут анализировать входные потоки. Это удобная функция при работе с большими шестнадцатеричными входными данными.

stefan.schwetschke
источник
Пример Guava не совсем работает так, как написано - это должно быть, base16().lowerCase().decode(...)если у вас есть шестнадцатеричные цифры нижнего регистра. docs.guava-libraries.googlecode.com/git/javadoc/com/google/…
Питер ДеГлоппер, 01
@PeterDeGlopper Хороший вывод, я обновил ответ, поэтому теперь код обрабатывает строки как с нижним, так и с верхним регистром.
stefan.schwetschke 02
1
javax.xml.bindбыл, к сожалению, удален в Java 9.
randomdude999
7

Вы можете использовать класс Java UUID для хранения этих значений вместо байтовых массивов:

UUID

public UUID(long mostSigBits,
            long leastSigBits)

Создает новый UUID, используя указанные данные. mostSigBits используется для 64 старших разрядов UUID, а lessSigBits становится 64 младшими битами UUID.

Джон
источник
3

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

    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 };
    }
}
ZMitton
источник
2

Что касается чистого процесса, вы можете использовать объект ByteArrayOutputStream ...

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

// записываем все значения в bObj одно за другим, используя

bObj.write(byte value)

// когда закончите, вы можете получить byte [], используя

CDRIVES = bObj.toByteArray();

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

ПРИМЕЧАНИЕ. Это не эффективное решение, если у вас действительно небольшой массив.

Амит
источник
2

Наименьший внутренний тип, который во время компиляции может быть назначен шестнадцатеричными числами, - это 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;
}
Сэм Гинрич
источник
1

В этом случае я предпочитаю использовать org.apache.commons.codec.binary.Hexкоторый имеет полезные API для преобразования между Stringшестнадцатеричным y и двоичным. Например:

  1. Hex.decodeHex(char[] data)который выдает a, DecoderExceptionесли в массиве есть не шестнадцатеричные символы или если есть нечетное количество символов.

  2. Hex.encodeHex(byte[] data)является аналогом метода декодирования, описанного выше, и выводит расширение char[].

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

Применение: Hex.decodeHex("dd645a2564cbe648c8336d2be5eafaa6".toCharArray())

rbrtl
источник
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");
Джон МакКлейн
источник
-2
private static final int[] CDRIVES = new int[] {0xe0, 0xf4, ...};

и после обращения конвертируем в байт.

Фрэнки
источник