Como converter uma matriz de bytes em uma sequência hexadecimal em Java?

649

Eu tenho uma matriz de bytes cheia de números hexadecimais e imprimi-la da maneira mais fácil é inútil, porque há muitos elementos não imprimíveis. O que eu preciso é o código hexadecimal exato na forma de:3a5f771c

Andre
fonte
12
Por que não tentar primeiro e nos mostrar o que você tem? Você não tem nada a perder e tudo a ganhar. O número inteiro tem um toHexString(...)método que pode ajudar se é isso que você está procurando. Também é String.format(...)possível fazer alguns truques de formatação usando a %2xsequência de códigos.
Hovercraft Cheio De Enguias
"O que eu preciso é do código hexadecimal exato na forma de: 3a5f771c ..." - você solicitou uma forma exata, mas não forneceu um exemplo exato. Seguindo o que você forneceu, converta os quatro primeiros bytes em uma sequência e concatene as elipses na sequência.
JWW
1
Com a ajuda do fluxo no Java 8, ele pode ser simplesmente implementado como: String estática byteArrayToHex (byte [] a) {retorne IntStream.range (0, comprimento) .mapToObj (i -> String.format ("% 02x ", a [i])) .reduce ((acc, v) -> acc +" "+ v) .get (); }
tibetty 10/10/1919

Respostas:

901

A partir da discussão aqui , e especialmente desta resposta, esta é a função que atualmente uso:

private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes) {
    char[] hexChars = new char[bytes.length * 2];
    for (int j = 0; j < bytes.length; j++) {
        int v = bytes[j] & 0xFF;
        hexChars[j * 2] = HEX_ARRAY[v >>> 4];
        hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
    }
    return new String(hexChars);
}

Meus próprios benchmarks minúsculos (um milhão de bytes por mil vezes, 256 bytes 10 milhões de vezes) mostraram que era muito mais rápido do que qualquer outra alternativa, cerca da metade do tempo em arrays longos. Comparado à resposta da qual tirei, alternar para operações bit a bit - como sugerido na discussão - reduziu em cerca de 20% o tempo para arrays longos. (Edit: Quando digo que é mais rápido que as alternativas, quero dizer o código alternativo oferecido nas discussões. O desempenho é equivalente ao Commons Codec, que usa código muito semelhante.)

Versão 2k20, com relação às seqüências compactas Java 9:

private static final byte[] HEX_ARRAY = "0123456789ABCDEF".toByteArray();
public static String bytesToHex(byte[] bytes) {
    byte[] hexChars = new byte[bytes.length * 2];
    for (int j = 0; j < bytes.length; j++) {
        int v = bytes[j] & 0xFF;
        hexChars[j * 2] = HEX_ARRAY[v >>> 4];
        hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
    }
    return new String(hexChars, StandardCharsets.UTF_8);
}
maybeWeCouldStealAVan
fonte
266
Acabei de encontrar javax.xml.bind.DataTypeConverter , parte da distribuição padrão. Por que isso não ocorre quando você coloca esse tipo de problema no Google? Muitas ferramentas úteis, incluindo String printHexBinary(byte[])e byte[] parseHexBinary(String). printHexBinaryé, no entanto, muito (2x) mais lento que a função nesta resposta. (Eu verifiquei a fonte; ela usa a stringBuilder. parseHexBinaryUsa uma matriz.) Na verdade, para a maioria dos propósitos, é rápido o suficiente e você provavelmente já a possui.
maybeWeCouldStealAVan
75
+1 para a resposta desde Android não tem DataTypeConverter
Vaiden
7
@maybeWeCouldStealAVan: O JDK 7 agora é de código aberto. Devemos enviar um patch para melhorar o desempenho printHexBinary?
Kevinarpe
3
@maybeWeCouldStealAVan, você poderia explicar como isso funciona. Eu sigo na maioria das vezes, mas gosto muito de entender o que está acontecendo ao usar código. Obrigado!
JjNford 12/07
24
javax.xml.bind.DataTypeConverterestá a ser removido a partir de Java 11.
O Empalador
421

A biblioteca do Apache Commons Codec tem uma classe Hex para fazer exatamente esse tipo de trabalho.

import org.apache.commons.codec.binary.Hex;

String foo = "I am a string";
byte[] bytes = foo.getBytes();
System.out.println( Hex.encodeHexString( bytes ) );
chooban
fonte
12
@cytinus - Meu voto negativo ocorreu há 4 meses, então não tenho muita certeza do que estava pensando, mas provavelmente estava me opondo ao tamanho da biblioteca. Esta é uma pequena função dentro do programa; não é necessário adicionar uma biblioteca tão volumosa ao projeto para executá-la.
ArtOfWarfare
6
@ArtOfWarefare Concordo, por isso, em vez de import org.apache.commons.codec.*;você poderia fazerimport org.apache.commons.codec.binary.Hex;
Cytinus
12
@ArtOfWarfare Eu tenho que discordar. A única coisa terrível é que as bibliotecas do apache commons não são incluídas por padrão no JRE e JDK. Existem algumas bibliotecas que são tão úteis que realmente deveriam estar no seu caminho de classe por padrão, e essa é uma delas.
precisa saber é o seguinte
29
Eu recomendo que esta resposta seja trocada como a resposta principal. Sempre vote para usar uma biblioteca de código-fonte aberto bem testada e com desempenho sobre um código personalizado que não melhora.
Dmitriy Likhten 26/02
6
Ou, caso você use BouncyCastle ( org.bouncycastle: bcprov-jdk15on ), você pode usar esta classe org.bouncycastle.util.encoders.HexString toHexString(byte[] data)
:,
320

O método javax.xml.bind.DatatypeConverter.printHexBinary(), parte da Arquitetura Java para Ligação XML (JAXB) , era uma maneira conveniente de converter a byte[]em uma sequência hexadecimal. A DatatypeConverterclasse também incluiu muitos outros métodos úteis de manipulação de dados.

No Java 8 e versões anteriores, o JAXB fazia parte da biblioteca padrão do Java. Foi preterido no Java 9 e removido com o Java 11 , como parte de um esforço para mover todos os pacotes Java EE para suas próprias bibliotecas. É uma longa história . Agora, javax.xml.bindnão existe, e se você deseja usar o JAXB, que contém DatatypeConverter, precisará instalar a API JAXB e o JAXB Runtime do Maven.

Exemplo de uso:

byte bytes[] = {(byte)0, (byte)0, (byte)134, (byte)0, (byte)61};
String hex = javax.xml.bind.DatatypeConverter.printHexBinary(bytes);

Vai resultar em:

000086003D

Esta resposta é a mesma que esta .

PhoneixS
fonte
13
Uma boa solução, embora infelizmente não seja válida no Android.
Kazriko
@Kazriko, talvez você queira ler code.google.com/p/dalvik/wiki/JavaxPackages . É uma maneira de obter classes javax no Android. Mas se você deseja converter apenas em hexadecimal, não vale a pena.
PhoneixS
13
DatatypeConverter não acessível a partir de JDK 9 é
pmcollins
3
@PhoneixS Ele ainda está lá, mas não faz parte do tempo de execução padrão (devido aos módulos Java 9).
Spotlight
2
não confie no javax.xml.bind, ele compila bem, mas não pode ser encontrado em tempo de execução. se o fizer, estar preparado para lidar java.lang.NoClassDefFoundError
Dmitry
227

Solução mais simples, sem bibliotecas externas, sem constantes de dígitos:

public static String byteArrayToHex(byte[] a) {
   StringBuilder sb = new StringBuilder(a.length * 2);
   for(byte b: a)
      sb.append(String.format("%02x", b));
   return sb.toString();
}
Ponteiro nulo
fonte
14
Isso é muito lento, em média 1000 vezes mais lento (com 162 bytes) do que o da resposta superior. Evite usar String.Format se o desempenho for importante.
pt123
8
Talvez devagar. É bom para coisas que acontecem ocasionalmente, como login ou similar.
Ponteiro Nulo
29
Se estiver lento, e daí? No meu caso de uso, é apenas para uma instrução de depuração, então obrigado por este fragmento de código.
vikingsteve
8
Reutilizar uma biblioteca incluindo arquivos JAR extras de várias dezenas de kB não seria exatamente eficiente se tudo o que você precisa é dessa função (em algumas plataformas como Android, todo o Jar é incluído no aplicativo final). E, às vezes, um código mais curto e mais claro é melhor quando o desempenho não é necessário.
personne3000
2
@ personne3000, talvez, mas nesse caso você precisa de suporte ao fluxo, não um recurso de chamada única. este é fácil de entender e lembrar, e portanto manter.
Maarten Bodewes
59

Uma solução Goiaba, para ser completo:

import com.google.common.io.BaseEncoding;
...
byte[] bytes = "Hello world".getBytes(StandardCharsets.UTF_8);
final String hex = BaseEncoding.base16().lowerCase().encode(bytes);

Agora hexé "48656c6c6f20776f726c64".

Stephan202
fonte
No Guava você também pode usar new HashCode(bytes).toString().
precisa saber é o seguinte
1
A partir da goiaba 22.0 éHashCode.fromBytes(checksum).toString()
Devstr
43

Este oneliner simples funciona para mim.
String result = new BigInteger(1, inputBytes).toString(16);
EDIT - Usar isso removerá os zeros à esquerda, mas ele trabalhou no meu caso de uso. Obrigado @Voicu por apontar

everconfusedGuy
fonte
56
Este oneliner descarta zero bytes à esquerda.
Voicu
@ Voicu ... E adicionará um zero inicial 50% do tempo.
Maarten Bodewes
27

Aqui estão algumas opções comuns ordenadas de simples (one-liner) a complexas (enorme biblioteca). Se você está interessado em desempenho, consulte os micro benchmarks abaixo.

Opção 1: snippet de código - Simples

Uma solução muito simples é usar a BigIntegerrepresentação hexadecimal da:

new BigInteger(1, someByteArray).toString(16)

Observe que, como ele lida com números que não sejam seqüências de bytes arbitrárias , ele omitirá zeros à esquerda - isso pode ou não ser o que você deseja (por exemplo, 000AE3vs 0AE3para uma entrada de 3 bytes). Isso também é muito lento, cerca de 100x mais lento em comparação com a próxima opção.

Opção 2: snippet de código - avançado

Aqui está um trecho de código completo, com cópia e colável que suporta maiúsculas / minúsculas e endianness . Ele é otimizado para minimizar a complexidade da memória e maximizar o desempenho e deve ser compatível com todas as versões modernas do Java (5+).

private static final char[] LOOKUP_TABLE_LOWER = new char[]{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66};
private static final char[] LOOKUP_TABLE_UPPER = new char[]{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46};

public static String encode(byte[] byteArray, boolean upperCase, ByteOrder byteOrder) {

    // our output size will be exactly 2x byte-array length
    final char[] buffer = new char[byteArray.length * 2];

    // choose lower or uppercase lookup table
    final char[] lookup = upperCase ? LOOKUP_TABLE_UPPER : LOOKUP_TABLE_LOWER;

    int index;
    for (int i = 0; i < byteArray.length; i++) {
        // for little endian we count from last to first
        index = (byteOrder == ByteOrder.BIG_ENDIAN) ? i : byteArray.length - i - 1;

        // extract the upper 4 bit and look up char (0-A)
        buffer[i << 1] = lookup[(byteArray[index] >> 4) & 0xF];
        // extract the lower 4 bit and look up char (0-A)
        buffer[(i << 1) + 1] = lookup[(byteArray[index] & 0xF)];
    }
    return new String(buffer);
}

public static String encode(byte[] byteArray) {
    return encode(byteArray, false, ByteOrder.BIG_ENDIAN);
}

O código fonte completo com licença e decodificador Apache v2 pode ser encontrado aqui .

Opção 3: Usando uma pequena biblioteca otimizada: bytes-java

Enquanto trabalhava no meu projeto anterior, criei este pequeno kit de ferramentas para trabalhar com bytes em Java. Não possui dependências externas e é compatível com Java 7+. Inclui, entre outros, um HEX en / decodificador muito rápido e bem testado:

import at.favre.lib.bytes.Bytes;
...
Bytes.wrap(someByteArray).encodeHex()

Você pode conferir no Github: bytes-java .

Opção 4: Apache Commons Codec

Claro que existem os bons codecs comuns . ( avisando a opinião adiante ) Enquanto trabalhava no projeto descrito acima, analisei o código e fiquei bastante decepcionado; muitos códigos desorganizados duplicados, codecs obsoletos e exóticos provavelmente só são úteis para muito poucas implementações lentas e bem projetadas e de engenharia de codecs populares (especificamente Base64). Portanto, eu tomaria uma decisão informada se você quiser usá-lo ou como alternativa. De qualquer forma, se você ainda quiser usá-lo, aqui está um trecho de código:

import org.apache.commons.codec.binary.Hex;
...
Hex.encodeHexString(someByteArray));

Opção 5: Google Guava

Mais frequentemente, você já tem o Goiaba como uma dependência. Se sim, basta usar:

import com.google.common.io.BaseEncoding;
...
BaseEncoding.base16().lowerCase().encode(someByteArray);

Opção 6: Spring Security

Se você usar a estrutura do Spring com o Spring Security, poderá usar o seguinte:

import org.springframework.security.crypto.codec.Hex
...
new String(Hex.encode(someByteArray));

Opção 7: Castelo Inflável

Se você já usa a estrutura de segurança Bouncy Castle, pode usar seu Hexutilitário:

import org.bouncycastle.util.encoders.Hex;
...
Hex.toHexString(someByteArray);

Opção Realmente Não 8: Compatibilidade com Java 9+ ou 'Não use JAXBs javax / xml / bind / DatatypeConverter'

Nas versões anteriores do Java (8 e abaixo), o código Java para JAXB foi incluído como dependência de tempo de execução. Desde a modularização do Java 9 e do Jigsaw, seu código não pode acessar outro código fora do módulo sem declaração explícita. Esteja ciente de que você recebe uma exceção como:

java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException

ao executar em uma JVM com Java 9+. Nesse caso, mude as implementações para qualquer uma das alternativas acima. Veja também esta questão .


Micro Benchmarks

Aqui estão os resultados de um simples micro benchmark JMH que codifica matrizes de bytes de tamanhos diferentes . Os valores são operações por segundo, portanto, quanto maior, melhor. Observe que os micro benchmarks muitas vezes não representam o comportamento do mundo real, portanto, leve esses resultados com um pouco de sal.

| Name (ops/s)         |    16 byte |    32 byte |  128 byte | 0.95 MB |
|----------------------|-----------:|-----------:|----------:|--------:|
| Opt1: BigInteger     |  2,088,514 |  1,008,357 |   133,665 |       4 |
| Opt2/3: Bytes Lib    | 20,423,170 | 16,049,841 | 6,685,522 |     825 |
| Opt4: Apache Commons | 17,503,857 | 12,382,018 | 4,319,898 |     529 |
| Opt5: Guava          | 10,177,925 |  6,937,833 | 2,094,658 |     257 |
| Opt6: Spring         | 18,704,986 | 13,643,374 | 4,904,805 |     601 |
| Opt7: BC             |  7,501,666 |  3,674,422 | 1,077,236 |     152 |
| Opt8: JAX-B          | 13,497,736 |  8,312,834 | 2,590,940 |     346 |

Especificações: JDK 8u202, i7-7700K, Win10, 24 GB de RAM. Veja a referência completa aqui .

Patrick Favre
fonte
25

Use a classe DataTypeConverterjavax.xml.bind.DataTypeConverter

String hexString = DatatypeConverter.printHexBinary(bytes[] raw);

Coral
fonte
2
Classe removida no Java 11. Consulte: JEP 320: Remova os módulos Java EE e CORBA
Basil Bourque
21

Eu usaria algo assim para comprimento fixo, como hashes:

md5sum = String.format("%032x", new BigInteger(1, md.digest()));
Usagi Miyamoto
fonte
2
Obrigado, isso é tão conciso e apropriado.
Deepan Prabhu Babu
18

Encontrei três maneiras diferentes aqui: http://www.rgagnon.com/javadetails/java-0596.html

O mais elegante, como ele também observa, acho que é este:

static final String HEXES = "0123456789ABCDEF";
public static String getHex( byte [] raw ) {
    if ( raw == null ) {
        return null;
    }
    final StringBuilder hex = new StringBuilder( 2 * raw.length );
    for ( final byte b : raw ) {
        hex.append(HEXES.charAt((b & 0xF0) >> 4))
            .append(HEXES.charAt((b & 0x0F)));
    }
    return hex.toString();
}
Michael Bisbjerg
fonte
Outros métodos estavam em execução no meu exemplo de 64 bytes em 5ms, este é executado em 0ms. Provavelmente é melhor por falta de outras funções String, como formato.
Joseph Lust
if (raw == null) return nullnão falha rápido. Por que você usaria uma nullchave?
Maarten Bodewes
Suponho que seja um hábito inserir validações. Nesse caso, evitamos qualquer exceção de referência nula e deixamos que o chamador lide com dados incorretos.
Michael Bisbjerg 27/02
16

Com o menor custo de armazenamento da tabela de pesquisa, essa implementação é simples e muito rápida.

 private static final char[] BYTE2HEX=(
    "000102030405060708090A0B0C0D0E0F"+
    "101112131415161718191A1B1C1D1E1F"+
    "202122232425262728292A2B2C2D2E2F"+
    "303132333435363738393A3B3C3D3E3F"+
    "404142434445464748494A4B4C4D4E4F"+
    "505152535455565758595A5B5C5D5E5F"+
    "606162636465666768696A6B6C6D6E6F"+
    "707172737475767778797A7B7C7D7E7F"+
    "808182838485868788898A8B8C8D8E8F"+
    "909192939495969798999A9B9C9D9E9F"+
    "A0A1A2A3A4A5A6A7A8A9AAABACADAEAF"+
    "B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF"+
    "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF"+
    "D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF"+
    "E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF"+
    "F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF").toCharArray();
   ; 

  public static String getHexString(byte[] bytes) {
    final int len=bytes.length;
    final char[] chars=new char[len<<1];
    int hexIndex;
    int idx=0;
    int ofs=0;
    while (ofs<len) {
      hexIndex=(bytes[ofs++] & 0xFF)<<1;
      chars[idx++]=BYTE2HEX[hexIndex++];
      chars[idx++]=BYTE2HEX[hexIndex];
    }
    return new String(chars);
  }
higginse
fonte
6
Por que não inicializar a BYTE2HEXmatriz com um forciclo simples ?
icza
@icza Isso é possível com um campo final estático (também conhecido como constante)?
Nevelis 06/07/19
1
@evelevel Pode ser atribuído em um static { }bloco.
131318
1
@icza porque é mais rápido codificar uma tabela de pesquisa do que gerá-la. Aqui, a complexidade da memória é trocada com a complexidade do tempo, ou seja. precisa de mais memória, mas mais rápido (cada assim ligeiramente em ambas as extremidades)
Patrick Favre
8

Que tal agora?

    String byteToHex(final byte[] hash)
    {
        Formatter formatter = new Formatter();
        for (byte b : hash)
        {
            formatter.format("%02x", b);
        }
        String result = formatter.toString();
        formatter.close();
        return result;
    }
Manan Bakshi
fonte
3

Não precisamos usar nenhuma biblioteca externa ou escrever código com base em loops e constantes.
Basta isso:

byte[] theValue = .....
String hexaString = new BigInteger(1, theValue).toString(16);
Marian
fonte
1
Isso é muito semelhante à resposta de everconfusedGuy.
Scratte
2

Eu prefiro usar isso:

final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes, int offset, int count) {
    char[] hexChars = new char[count * 2];
    for ( int j = 0; j < count; j++ ) {
        int v = bytes[j+offset] & 0xFF;
        hexChars[j * 2] = hexArray[v >>> 4];
        hexChars[j * 2 + 1] = hexArray[v & 0x0F];
    }
    return new String(hexChars);
}

É uma adaptação um pouco mais flexível da resposta aceita. Pessoalmente, mantenho a resposta aceita e essa sobrecarga, utilizáveis ​​em mais contextos.

Bamaco
fonte
A pergunta original era para byte [] para String. Procure hexadecimal em bytes [] ou faça uma pergunta diferente, @NonExistent.
Bamaco
2

Normalmente, uso o seguinte método para a declaração debuf, mas não sei se é a melhor maneira de fazê-lo ou não

private static String digits = "0123456789abcdef";

public static String toHex(byte[] data){
    StringBuffer buf = new StringBuffer();
    for (int i = 0; i != data.length; i++)
    {
        int v = data[i] & 0xff;
        buf.append(digits.charAt(v >> 4));
        buf.append(digits.charAt(v & 0xf));
    }
    return buf.toString();
}
Snox
fonte
2
Se o seu debuffer tem um dia ruim, tente cluing em StringBuilder instanciação com um número de caracteres para o suporte: StringBuilder buf = new StringBuilder(data.length * 2);.
13136
2

Ok, então existem várias maneiras de fazer isso, mas se você decidir usar uma biblioteca, sugiro bisbilhotar no seu projeto para ver se algo foi implementado em uma biblioteca que já faz parte do seu projeto antes de adicionar uma nova biblioteca. apenas para fazer isso. Por exemplo, se você ainda não possui

org.apache.commons.codec.binary.Hex

talvez você tenha ...

org.apache.xerces.impl.dv.util.HexBin

Aaron Cooley
fonte
2

Se você estiver usando a estrutura Spring Security, poderá usar:

import org.springframework.security.crypto.codec.Hex

final String testString = "Test String";
final byte[] byteArray = testString.getBytes();
System.out.println(Hex.encode(byteArray));
java-addict301
fonte
2

Adicionar uma jarra de utilidade para uma função simples não é uma boa opção. Em vez disso, monte suas próprias classes de utilitários. é possível uma implementação mais rápida.

public class ByteHex {

    public static int hexToByte(char ch) {
        if ('0' <= ch && ch <= '9') return ch - '0';
        if ('A' <= ch && ch <= 'F') return ch - 'A' + 10;
        if ('a' <= ch && ch <= 'f') return ch - 'a' + 10;
        return -1;
    }

    private static final String[] byteToHexTable = new String[]
    {
        "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0A", "0B", "0C", "0D", "0E", "0F",
        "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1A", "1B", "1C", "1D", "1E", "1F",
        "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2A", "2B", "2C", "2D", "2E", "2F",
        "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3A", "3B", "3C", "3D", "3E", "3F",
        "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4A", "4B", "4C", "4D", "4E", "4F",
        "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5A", "5B", "5C", "5D", "5E", "5F",
        "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6A", "6B", "6C", "6D", "6E", "6F",
        "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7A", "7B", "7C", "7D", "7E", "7F",
        "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8A", "8B", "8C", "8D", "8E", "8F",
        "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9A", "9B", "9C", "9D", "9E", "9F",
        "A0", "A1", "A2", "A3", "A4", "A5", "A6", "A7", "A8", "A9", "AA", "AB", "AC", "AD", "AE", "AF",
        "B0", "B1", "B2", "B3", "B4", "B5", "B6", "B7", "B8", "B9", "BA", "BB", "BC", "BD", "BE", "BF",
        "C0", "C1", "C2", "C3", "C4", "C5", "C6", "C7", "C8", "C9", "CA", "CB", "CC", "CD", "CE", "CF",
        "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", "D8", "D9", "DA", "DB", "DC", "DD", "DE", "DF",
        "E0", "E1", "E2", "E3", "E4", "E5", "E6", "E7", "E8", "E9", "EA", "EB", "EC", "ED", "EE", "EF",
        "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "FA", "FB", "FC", "FD", "FE", "FF"
    };

    private static final String[] byteToHexTableLowerCase = new String[]
    {
        "00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "0a", "0b", "0c", "0d", "0e", "0f",
        "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "1a", "1b", "1c", "1d", "1e", "1f",
        "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "2a", "2b", "2c", "2d", "2e", "2f",
        "30", "31", "32", "33", "34", "35", "36", "37", "38", "39", "3a", "3b", "3c", "3d", "3e", "3f",
        "40", "41", "42", "43", "44", "45", "46", "47", "48", "49", "4a", "4b", "4c", "4d", "4e", "4f",
        "50", "51", "52", "53", "54", "55", "56", "57", "58", "59", "5a", "5b", "5c", "5d", "5e", "5f",
        "60", "61", "62", "63", "64", "65", "66", "67", "68", "69", "6a", "6b", "6c", "6d", "6e", "6f",
        "70", "71", "72", "73", "74", "75", "76", "77", "78", "79", "7a", "7b", "7c", "7d", "7e", "7f",
        "80", "81", "82", "83", "84", "85", "86", "87", "88", "89", "8a", "8b", "8c", "8d", "8e", "8f",
        "90", "91", "92", "93", "94", "95", "96", "97", "98", "99", "9a", "9b", "9c", "9d", "9e", "9f",
        "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "aa", "ab", "ac", "ad", "ae", "af",
        "b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9", "ba", "bb", "bc", "bd", "be", "bf",
        "c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "ca", "cb", "cc", "cd", "ce", "cf",
        "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "da", "db", "dc", "dd", "de", "df",
        "e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8", "e9", "ea", "eb", "ec", "ed", "ee", "ef",
        "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "fa", "fb", "fc", "fd", "fe", "ff"
    };

    public static String byteToHex(byte b){
        return byteToHexTable[b & 0xFF];
    }

    public static String byteToHex(byte[] bytes){
        if(bytes == null) return null;
        StringBuilder sb = new StringBuilder(bytes.length*2);
        for(byte b : bytes) sb.append(byteToHexTable[b & 0xFF]);
        return sb.toString();
    }

    public static String byteToHex(short[] bytes){
        StringBuilder sb = new StringBuilder(bytes.length*2);
        for(short b : bytes) sb.append(byteToHexTable[((byte)b) & 0xFF]);
        return sb.toString();
    }

    public static String byteToHexLowerCase(byte[] bytes){
        StringBuilder sb = new StringBuilder(bytes.length*2);
        for(byte b : bytes) sb.append(byteToHexTableLowerCase[b & 0xFF]);
        return sb.toString();
    }

    public static byte[] hexToByte(String hexString) {
        if(hexString == null) return null;
        byte[] byteArray = new byte[hexString.length() / 2];
        for (int i = 0; i < hexString.length(); i += 2) {
            byteArray[i / 2] = (byte) (hexToByte(hexString.charAt(i)) * 16 + hexToByte(hexString.charAt(i+1)));
        }
        return byteArray;
    }

    public static byte hexPairToByte(char ch1, char ch2) {
        return (byte) (hexToByte(ch1) * 16 + hexToByte(ch2));
    }


}
Krishna Telgave
fonte
1

Uma pequena variante da solução proposta por @maybewecouldstealavan, que permite agrupar visualmente N bytes na sequência hexadecimal de saída:

 final static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
 final static char BUNDLE_SEP = ' ';

public static String bytesToHexString(byte[] bytes, int bundleSize /*[bytes]*/]) {
        char[] hexChars = new char[(bytes.length * 2) + (bytes.length / bundleSize)];
        for (int j = 0, k = 1; j < bytes.length; j++, k++) {
                int v = bytes[j] & 0xFF;
                int start = (j * 2) + j/bundleSize;

                hexChars[start] = HEX_ARRAY[v >>> 4];
                hexChars[start + 1] = HEX_ARRAY[v & 0x0F];

                if ((k % bundleSize) == 0) {
                        hexChars[start + 2] = BUNDLE_SEP;
                }   
        }   
        return new String(hexChars).trim();    
}

Isso é:

bytesToHexString("..DOOM..".toCharArray().getBytes(), 2);
2E2E 444F 4F4D 2E2E

bytesToHexString("..DOOM..".toCharArray().getBytes(), 4);
2E2E444F 4F4D2E2E
Campa
fonte
1

Não é possível encontrar nesta solução nenhuma solução que não

  1. Use um loop
  2. Use javax.xml.bind.DatatypeConverter que compila bem, mas geralmente lança java.lang.NoClassDefFoundError em tempo de execução.

Aqui está uma solução que não possui as falhas acima (nenhuma promessa é minha, mas não tem outras falhas)

import java.math.BigInteger;

import static java.lang.System.out;
public final class App2 {
    // | proposed solution.
    public static String encode(byte[] bytes) {          
        final int length = bytes.length;

        // | BigInteger constructor throws if it is given an empty array.
        if (length == 0) {
            return "00";
        }

        final int evenLength = (int)(2 * Math.ceil(length / 2.0));
        final String format = "%0" + evenLength + "x";         
        final String result = String.format (format, new BigInteger(bytes));

        return result;
    }

    public static void main(String[] args) throws Exception {
        // 00
        out.println(encode(new byte[] {})); 

        // 01
        out.println(encode(new byte[] {1})); 

        //203040
        out.println(encode(new byte[] {0x20, 0x30, 0x40})); 

        // 416c6c20796f75722062617365206172652062656c6f6e6720746f2075732e
        out.println(encode("All your base are belong to us.".getBytes()));
    }
}   

Não consegui obter isso em 62 opcodes, mas se você puder viver sem preenchimento de 0, caso o primeiro byte seja menor que 0x10, a solução a seguir usará apenas 23 opcodes. Realmente mostra como soluções "fáceis de implementar" como "pad com zero se o comprimento da string for ímpar" podem ficar bem caras se uma implementação nativa ainda não estiver disponível (ou, neste caso, se o BigInteger tivesse a opção de prefixar zeros em para sequenciar).

public static String encode(byte[] bytes) {          
    final int length = bytes.length;

    // | BigInteger constructor throws if it is given an empty array.
    if (length == 0) {
        return "00";
    }

    return new BigInteger(bytes).toString(16);
}
Dmitry
fonte
1

Minha solução é baseada na solução de maybeWeCouldStealAVan, mas não depende de nenhuma tabela de pesquisa adicionalmente alocada. Ele não usa nenhuma ferramenta de conversão de 'int-to-char' (na verdade Character.forDigit(), executa algumas comparações para verificar qual é realmente o dígito) e, portanto, pode ser um pouco mais lenta. Por favor, sinta-se livre para usá-lo onde quiser. Felicidades.

public static String bytesToHex(final byte[] bytes)
{
    final int numBytes = bytes.length;
    final char[] container = new char[numBytes * 2];

    for (int i = 0; i < numBytes; i++)
    {
        final int b = bytes[i] & 0xFF;

        container[i * 2] = Character.forDigit(b >>> 4, 0x10);
        container[i * 2 + 1] = Character.forDigit(b & 0xF, 0x10);
    }

    return new String(container);
}
Netherwire
fonte
0

// Mudar bytes é mais eficiente // Você também pode usar este

public static String getHexString (String s) 
{
    byte[] buf = s.getBytes();

    StringBuffer sb = new StringBuffer();

    for (byte b:buf)
    {
        sb.append(String.format("%x", b));
    }


        return sb.toString();
}
BluePurse
fonte
0

Se você está procurando uma matriz de bytes exatamente como esta para python, eu converti essa implementação Java em python.

class ByteArray:

@classmethod
def char(cls, args=[]):
    cls.hexArray = "0123456789ABCDEF".encode('utf-16')
    j = 0
    length = (cls.hexArray)

    if j < length:
        v = j & 0xFF
        hexChars = [None, None]
        hexChars[j * 2] = str( cls.hexArray) + str(v)
        hexChars[j * 2 + 1] = str(cls.hexArray) + str(v) + str(0x0F)
        # Use if you want...
        #hexChars.pop()

    return str(hexChars)

array = ByteArray()
print array.char(args=[])
SkippsDev
fonte
0
  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;
  } 
田 咖啡
fonte
0

Aqui está uma java.util.Base64implementação do tipo (parcial), não é bonita?

public class Base16/*a.k.a. Hex*/ {
    public static class Encoder{
        private static char[] toLowerHex={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
        private static char[] toUpperHex={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
        private boolean upper;
        public Encoder(boolean upper) {
            this.upper=upper;
        }
        public String encode(byte[] data){
            char[] value=new char[data.length*2];
            char[] toHex=upper?toUpperHex:toLowerHex;
            for(int i=0,j=0;i<data.length;i++){
                int octet=data[i]&0xFF;
                value[j++]=toHex[octet>>4];
                value[j++]=toHex[octet&0xF];
            }
            return new String(value);
        }
        static final Encoder LOWER=new Encoder(false);
        static final Encoder UPPER=new Encoder(true);
    }
    public static Encoder getEncoder(){
        return Encoder.LOWER;
    }
    public static Encoder getUpperEncoder(){
        return Encoder.UPPER;
    }
    //...
}
fuweichin
fonte
0
private static String bytesToHexString(byte[] bytes, int length) {
        if (bytes == null || length == 0) return null;

        StringBuilder ret = new StringBuilder(2*length);

        for (int i = 0 ; i < length ; i++) {
            int b;

            b = 0x0f & (bytes[i] >> 4);
            ret.append("0123456789abcdef".charAt(b));

            b = 0x0f & bytes[i];
            ret.append("0123456789abcdef".charAt(b));
        }

        return ret.toString();
    }
kakopappa
fonte
0
Converts bytes data to hex characters

@param bytes byte array to be converted to hex string
@return byte String in hex format

private static String bytesToHex(byte[] bytes) {
    char[] hexChars = new char[bytes.length * 2];
    int v;
    for (int j = 0; j < bytes.length; j++) {
        v = bytes[j] & 0xFF;
        hexChars[j * 2] = HEX_ARRAY[v >>> 4];
        hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
    }
    return new String(hexChars);
}
Rajneesh Shukla
fonte