Jak przekonwertować tablicę bajtów na ciąg szesnastkowy w Javie?

649

Mam tablicę bajtów wypełnioną liczbami szesnastkowymi i drukowanie jej w prosty sposób jest dość bezcelowe, ponieważ istnieje wiele elementów, które nie mogą zostać wydrukowane. Potrzebuję dokładnego kodu szesnastkowego w postaci:3a5f771c

Andre
źródło
12
Dlaczego nie spróbować najpierw i pokazać nam, co masz. Nie masz nic do stracenia, a wszyscy do zyskania. Liczba całkowita ma toHexString(...)metodę, która może pomóc, jeśli tego właśnie szukasz. String.format(...)Można także wykonać kilka sztuczek formatowania przy użyciu ciągu %2xkodu.
Poduszkowiec pełen węgorzy
„Potrzebuję dokładnego kodu szesnastkowego w postaci: 3a5f771c ...” - poprosiłeś o dokładny formularz, ale nie podałeś dokładnego przykładu. Przechodząc do tego, co podałeś, przekonwertuj pierwsze cztery bajty na ciąg, a następnie połącz elipsy z ciągiem.
jww
1
Za pomocą strumienia w Javie 8 można go po prostu zaimplementować jako: static String byteArrayToHex (byte [] a) {return IntStream.range (0, a.length) .mapToObj (i -> String.format ("% 02x ", a [i])) .reduce ((acc, v) -> acc +" "+ v) .get (); }
tibetty,

Odpowiedzi:

900

Z dyskusji tutaj , a zwłaszcza z tej odpowiedzi, jest to funkcja, z której obecnie korzystam:

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

Moje własne małe testy porównawcze (milion bajtów tysiąc razy, 256 bajtów 10 milionów razy) wykazały, że jest on znacznie szybszy niż jakakolwiek inna alternatywa, o połowę krótszy na długich tablicach. W porównaniu z odpowiedzią, z której zacząłem, przejście na operacje bitowe --- jak sugerowano w dyskusji --- skróciło około 20% czasu na długie tablice. (Edycja: Kiedy mówię, że jest szybszy niż alternatywy, mam na myśli alternatywny kod oferowany w dyskusjach. Wydajność jest równoważna kodekowi Commons, który używa bardzo podobnego kodu).

Wersja 2k20 w odniesieniu do kompaktowych ciągów 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);
}
możeWeCouldStealAVan
źródło
266
Właśnie znalazłem javax.xml.bind.DataTypeConverter , część standardowej dystrybucji. Dlaczego to nie pojawia się, gdy Google ma tego rodzaju problem? Wiele pomocnych narzędzi, w tym String printHexBinary(byte[])i byte[] parseHexBinary(String). printHexBinaryjest jednak znacznie (2x) wolniejszy niż funkcja w tej odpowiedzi. (Sprawdziłem źródło; używa a stringBuilder. parseHexBinaryUżywa tablicy). Naprawdę jednak, dla większości celów jest wystarczająco szybki i prawdopodobnie już go masz.
możeWeCouldStealAVan
75
+1 za odpowiedź, ponieważ Android nie ma DataTypeConverter
Vaiden
7
@maybeWeCouldStealAVan: JDK 7 jest teraz oprogramowaniem typu open source. Powinniśmy przesłać łatkę, aby poprawić wydajność printHexBinary?
kevinarpe
3
@ maybeWeCouldStealAVan czy mógłbyś wyjaśnić, jak to działa. W większości śledzę, ale naprawdę lubię rozumieć, co dzieje się podczas używania kodu. Dzięki!
jjNford
24
javax.xml.bind.DataTypeConverterjest usuwany z Java 11.
The Impaler
421

Apache Commons Codec biblioteka ma Hex klasę dla robi tylko ten rodzaj pracy.

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

String foo = "I am a string";
byte[] bytes = foo.getBytes();
System.out.println( Hex.encodeHexString( bytes ) );
chooban
źródło
12
@cytinus - Moje głosowanie zdarzyło się 4 miesiące temu, więc nie jestem do końca pewien, o czym myślałem, ale prawdopodobnie sprzeciwiłem się rozmiarowi biblioteki. Jest to niewielka funkcja w programie; nie ma potrzeby dodawania tak dużej biblioteki do projektu, aby ją wykonać.
ArtOfWarfare
6
@ArtOfWarefare Zgadzam się, więc zamiast tego import org.apache.commons.codec.*;mógłbyś zrobićimport org.apache.commons.codec.binary.Hex;
cytinus
12
@ArtOfWarfare Muszę się nie zgodzić. Jedyną straszną rzeczą jest to, że biblioteki wspólne apache nie są domyślnie dołączone do JRE i JDK. Istnieje kilka bibliotek, które są tak przydatne, że naprawdę powinny domyślnie znajdować się na ścieżce zajęć i jest to jedna z nich.
corsiKa,
29
Bardzo polecam tę odpowiedź zamieniono na pierwszą odpowiedź. Zawsze głosuj, aby użyć dobrze przetestowanej, wydajnej biblioteki open source zamiast niestandardowego kodu, który go nie poprawia.
Dmitriy Likhten
6
Lub w przypadku korzystania z BouncyCastle ( org.bouncycastle: bcprov-jdk15on ), możesz użyć tej klasy: org.bouncycastle.util.encoders.Hexprzy użyciu tej metody:String toHexString(byte[] data)
Guillaume Husta
320

Ta metoda javax.xml.bind.DatatypeConverter.printHexBinary(), będąca częścią architektury Java Binding XML (JAXB) , była wygodnym sposobem konwersji byte[]łańcucha szesnastkowego. DatatypeConverterKlasa zawiera także wiele innych przydatnych metod manipulacji danymi.

W Javie 8 i wcześniejszych JAXB był częścią standardowej biblioteki Java. Został on przestarzały w Javie 9 i usunięty w Javie 11 , w ramach próby przeniesienia wszystkich pakietów Java EE do ich własnych bibliotek. To długa historia . Teraz javax.xml.bindnie istnieje, a jeśli chcesz użyć JAXB, który zawiera DatatypeConverter, musisz zainstalować JAXB API i JAXB Runtime z Maven.

Przykładowe użycie:

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

Spowoduje to:

000086003D

Ta odpowiedź jest taka sama jak ta .

PhoneixS
źródło
13
Dobre rozwiązanie, choć niestety nie jest prawidłowe w Androidzie.
Kazriko,
@Kazriko może chcesz przeczytać code.google.com/p/dalvik/wiki/JavaxPackages . Jest to sposób na przeniesienie klas javax na Androida. Ale jeśli chcesz tylko przekonwertować na hex, nie jest to warte kłopotu.
PhoneixS,
13
DatatypeConverter nie jest już dostępny od JDK 9
pmcollins
3
@PhoneixS Nadal istnieje, ale nie jest częścią domyślnego środowiska wykonawczego (z powodu modułów Java 9).
Spotlight
2
nie polegaj na javax.xml.bind, kompiluje się dobrze, ale nie można go znaleźć w czasie wykonywania. jeśli to zrobisz, przygotuj się na obsługę pliku java.lang.NoClassDefFoundError
Dmitry
227

Najprostsze rozwiązanie, brak zewnętrznych bibliotek lib, brak cyfr stałych:

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();
}
Wskaźnik Null
źródło
14
Jest to bardzo powolne, średnio 1000 razy wolniejsze (przy długości 162 bajtów) niż w odpowiedzi najwyższej. Unikaj używania String.Format, jeśli wydajność ma znaczenie.
pt123
8
Może powoli. Jest dobry na rzeczy, które dzieją się okazjonalnie, takie jak logowanie lub podobne.
Pointer Null
29
Jeśli jest wolny, co z tego? W moim przypadku jest to tylko instrukcja debugowania, więc dziękuję za ten fragment kodu.
vikingsteve
8
Ponowne użycie biblioteki przez dołączenie dodatkowych plików JAR o wielkości kilkudziesięciu kB nie byłoby dokładnie skuteczne, gdyby wszystko, czego potrzebujesz, to ta funkcja (na niektórych platformach, takich jak Android, cały Jar jest dołączany do aplikacji końcowej). A czasem krótszy i bardziej przejrzysty kod jest lepszy, gdy wydajność nie jest potrzebna.
personne3000
2
@ personne3000 może, ale w takim przypadku potrzebujesz wsparcia strumieniowego, a nie funkcji pojedynczego połączenia. ten jest łatwy do zrozumienia i zapamiętania, a zatem do utrzymania.
Maarten Bodewes
59

Rozwiązanie Guava dla kompletności:

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

Teraz hexjest "48656c6c6f20776f726c64".

Stephan202
źródło
W Guava możesz także użyć new HashCode(bytes).toString().
mfulton26
1
W wersji Guava 22.0 jest toHashCode.fromBytes(checksum).toString()
Devstr
43

Ten prosty oneliner działa dla mnie
String result = new BigInteger(1, inputBytes).toString(16);
EDYCJA - użycie tego spowoduje usunięcie zer wiodących, ale hej działał w moim przypadku użycia. Dzięki @Voicu za zwrócenie na to uwagi

everconfusedGuy
źródło
56
Ten oneliner usuwa wiodące zero bajtów.
Voicu
@ Voicu ... I doda 50% zera na początku.
Maarten Bodewes
27

Oto kilka typowych opcji uporządkowanych od prostych (jedno-liniowy) do złożonych (ogromna biblioteka). Jeśli jesteś zainteresowany wydajnością, zapoznaj się z mikro-testami poniżej.

Opcja 1: Fragment kodu - prosty

Jednym bardzo prostym rozwiązaniem jest użycie BigIntegerreprezentacji szesnastkowej:

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

Zauważ, że ponieważ obsługuje liczby, które nie są dowolnymi ciągami bajtów , pominie zera wiodące - może to być lub nie być to, czego chcesz (np. 000AE3Vs 0AE3dla danych 3-bajtowych). Jest to również bardzo wolne, około 100 razy wolniejsze w porównaniu do następnej opcji.

Opcja 2: fragment kodu - zaawansowane

Oto w pełni funkcjonalny, kopiowany i wklejany fragment kodu obsługujący wielkie / małe litery oraz endianness . Jest zoptymalizowany, aby zminimalizować złożoność pamięci i zmaksymalizować wydajność i powinien być kompatybilny ze wszystkimi nowoczesnymi wersjami 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);
}

Pełny kod źródłowy z licencją i dekoderem Apache v2 można znaleźć tutaj .

Opcja 3: Korzystanie z małej zoptymalizowanej biblioteki: bytes-java

Pracując nad poprzednim projektem, stworzyłem ten mały zestaw narzędzi do pracy z bajtami w Javie. Nie ma zewnętrznych zależności i jest kompatybilny z Javą 7+. Obejmuje między innymi bardzo szybki i dobrze przetestowany dekoder / dekoder HEX:

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

Możesz to sprawdzić na Github: bytes-java .

Opcja 4: kodek Apache Commons

Oczywiście są dobre kodeki wspólne . ( opinia ostrzegawcza przed nami ) Podczas pracy nad projektem opisanym powyżej przeanalizowałem kod i byłem bardzo rozczarowany; wiele zduplikowanych niezorganizowanych kodów, przestarzałe i egzotyczne kodeki są prawdopodobnie przydatne tylko dla bardzo nielicznych i dość skomplikowanych i powolnych implementacji popularnych kodeków (szczególnie Base64). W związku z tym podjąłbym świadomą decyzję, jeśli chcesz skorzystać z niej lub z alternatywy. W każdym razie, jeśli nadal chcesz go używać, oto fragment kodu:

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

Opcja 5: Google Guava

Najczęściej masz Guava jako zależność. Jeśli tak, użyj:

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

Opcja 6: Spring Security

Jeśli korzystasz ze struktury Spring z Spring Security , możesz użyć następujących opcji:

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

Opcja 7: dmuchany zamek

Jeśli korzystasz już z ram bezpieczeństwa Bouncy Castle , możesz użyć jego wykorzystania Hex:

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

Niezupełnie opcja 8: Zgodność z Java 9+ lub „Nie używaj JAXBs javax / xml / bind / DatatypeConverter”

W poprzednich wersjach Java (8 i niższych) kod Java dla JAXB był dołączany jako zależność środowiska wykonawczego. Od modularyzacji Java 9 i Jigsaw Twój kod nie może uzyskać dostępu do innego kodu poza modułem bez wyraźnej deklaracji. Więc pamiętaj, jeśli otrzymasz wyjątek, taki jak:

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

podczas pracy na JVM z Javą 9+. Jeśli tak, przełącz implementacje na dowolne z powyższych rozwiązań. Zobacz także to pytanie .


Mikroskopy

Oto wyniki z prostego macierzy bajtowej JMH do mikro-testów o różnych rozmiarach . Wartości są operacjami na sekundę, więc im wyższa, tym lepiej. Zauważ, że mikroskopy bardzo często nie reprezentują zachowania w świecie rzeczywistym, więc weź te wyniki z odrobiną soli.

| 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 |

Specyfikacja: JDK 8u202, i7-7700K, Win10, 24 GB RAM. Zobacz pełny test tutaj .

Patrick Favre
źródło
25

Użyj klasy DataTypeConverterjavax.xml.bind.DataTypeConverter

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

Koral
źródło
2
Klasa usunięta w Javie 11. Zobacz: JEP 320: Usuń moduły Java EE i CORBA
Basil Bourque,
21

Użyłbym czegoś takiego dla stałej długości, np. Skrótów:

md5sum = String.format("%032x", new BigInteger(1, md.digest()));
Usagi Miyamoto
źródło
2
Dziękuję, to jest tak zwięzłe i właściwe.
Deepan Prabhu Babu
17

Znalazłem tutaj trzy różne sposoby: http://www.rgagnon.com/javadetails/java-0596.html

Najbardziej elegancki, jak zauważa, myślę, że jest ten:

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
źródło
Inne metody działały na mojej 64-bajtowej próbce w 5ms, ta działa w 0ms. Prawdopodobnie najlepiej z powodu braku jakichkolwiek innych funkcji String, takich jak format.
Joseph Lust,
if (raw == null) return nullnie zawiedzie szybko. Dlaczego miałbyś kiedykolwiek używać nullklucza?
Maarten Bodewes
Przypuszczam, że zwyczajem jest sprawdzanie poprawności. W takim przypadku zapobiegamy wszelkim wyjątkom odniesienia Null i pozostawiamy to abonentowi wywołującemu złe dane.
Michael Bisbjerg
16

Przy niewielkich kosztach przechowywania tabeli odnośników ta implementacja jest prosta i bardzo szybka.

 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
źródło
6
Dlaczego nie zainicjować BYTE2HEXtablicy za pomocą prostego forcyklu?
icza
@icza Czy to w ogóle możliwe przy statycznym polu końcowym (aka stałym)?
nevelis
1
@nevelis Można go przypisać w static { }bloku.
マ ル ち ゃ ん だ よ
1
@icza, ponieważ szybsze kodowanie tabeli odnośników jest szybsze niż jej wygenerowanie. Tutaj złożoność pamięci jest handlowana ze złożonością czasową, tj. potrzebuje więcej pamięci, ale szybciej (co nieco na obu końcach)
Patrick Favre
8

Co powiesz na to?

    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
źródło
3

Nie musimy używać żadnej zewnętrznej biblioteki ani pisać kodu na podstawie pętli i stałych.
Wystarczy tylko to:

byte[] theValue = .....
String hexaString = new BigInteger(1, theValue).toString(16);
Marian
źródło
1
Jest to bardzo podobne do odpowiedzi EverconfusedGuy.
Scratte
2

Wolę użyć tego:

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

Jest to nieco bardziej elastyczne dostosowanie przyjętej odpowiedzi. Osobiście zachowuję zarówno akceptowaną odpowiedź, jak i to przeciążenie wraz z nią, do użytku w większej liczbie kontekstów.

Bamaco
źródło
Pierwotne pytanie dotyczyło bajtu [] do String. Spójrz szesnastkowo na bajty [] lub zadaj inne pytanie, @Nieistniejące.
Bamaco,
2

Zwykle używam następującej metody do instrukcji debuf, ale nie wiem, czy jest to najlepszy sposób, czy nie

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
źródło
2
Jeśli debuffer ma zły dzień, spróbuj cluing w StringBuilder instancji z liczbą znaków do wsparcia: StringBuilder buf = new StringBuilder(data.length * 2);.
greybeard
2

Ok, więc istnieje wiele sposobów, aby to zrobić, ale jeśli zdecydujesz się użyć biblioteki, radzę zajrzeć do twojego projektu, aby sprawdzić, czy coś zostało zaimplementowane w bibliotece, która jest już częścią twojego projektu przed dodaniem nowej biblioteki po prostu to zrobić. Na przykład, jeśli jeszcze tego nie masz

org.apache.commons.codec.binary.Hex

może masz ...

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

Aaron Cooley
źródło
2

Jeśli korzystasz ze środowiska Spring Security, możesz użyć:

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
źródło
2

Dodanie słoika narzędziowego dla prostej funkcji nie jest dobrą opcją. Zamiast tego montuj własne klasy narzędzi. możliwe jest szybsze wdrożenie.

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
źródło
1

Mały wariant rozwiązania zaproponowanego przez @maybewecouldstealavan, który pozwala wizualnie łączyć N bajtów razem w wyjściowym ciągu szesnastkowym:

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

To jest:

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

bytesToHexString("..DOOM..".toCharArray().getBytes(), 4);
2E2E444F 4F4D2E2E
Campa
źródło
1

Nie mogę znaleźć na tej stronie żadnego rozwiązania, które by tego nie zrobiło

  1. Użyj pętli
  2. Użyj javax.xml.bind.DatatypeConverter, który dobrze się kompiluje, ale często zgłasza java.lang.NoClassDefFoundError w czasie wykonywania.

Oto rozwiązanie, które nie ma wad powyżej (żadne moje obietnice nie mają innych wad)

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

Nie udało mi się uzyskać tego pod 62 kodami, ale jeśli możesz żyć bez zerowania w przypadku, gdy pierwszy bajt jest mniejszy niż 0x10, poniższe rozwiązanie używa tylko 23 kodów. Naprawdę pokazuje, jak rozwiązania „łatwe do samodzielnego wdrożenia”, takie jak „pad z zerem, jeśli długość łańcucha jest nieparzysta”, mogą stać się dość drogie, jeśli natywna implementacja nie jest już dostępna (lub w tym przypadku, jeśli BigInteger miał opcję prefiksu zerami w toString).

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
źródło
1

Moje rozwiązanie jest oparte na rozwiązaniu możeWeCouldStealAVan, ale nie opiera się na żadnych dodatkowych tabelach wyszukiwania. Nie używa hacków typu int-to-char (w rzeczywistości Character.forDigit()robi to, porównując, aby sprawdzić, czym naprawdę jest cyfra), a zatem może być nieco wolniejszy. Możesz go używać gdziekolwiek chcesz. Twoje zdrowie.

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
źródło
0

// Przesunięcie bajtów jest bardziej wydajne // Możesz także użyć tego

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
źródło
0

Jeśli szukasz tablicy bajtów dokładnie takiej jak dla Pythona, przekonwertowałem tę implementację Java na 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
źródło
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;
  } 
田 咖啡
źródło
0

Oto java.util.Base64implementacja przypominająca (częściowa), prawda?

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
źródło
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
źródło
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
źródło