Czy konwertować ciąg znaków zrzutu szesnastkowego na tablicę bajtów za pomocą Java?

372

Szukam sposobu na konwersję długiego łańcucha (ze zrzutu), który reprezentuje wartości szesnastkowe w tablicy bajtów.

Nie potrafiłbym sformułować tego lepiej niż osoba, która zadała to samo pytanie tutaj .

Ale aby zachować oryginalność, sformułuję to po swojemu: załóżmy, że mam ciąg "00A0BF", który chciałbym interpretować jako

byte[] {0x00,0xA0,0xBf}

co powinienem zrobić?

Jestem nowicjuszem w Javie i skończyłem na używaniu BigIntegerwiodących zer szesnastkowych. Ale myślę, że to brzydkie i jestem pewien, że brakuje mi czegoś prostego.

rafraf
źródło
Oswajałem się BigInteger tutaj .
John McClane,
FWIW String.getBytes()nie będzie działać tak, jak myślisz. Musiałem nauczyć się tego na własnej skórze. if ("FF".getBytes() != "ff".getBytes()) { System.out.println("Try again"); }
tir38

Odpowiedzi:

636

Oto rozwiązanie, które moim zdaniem jest lepsze niż jakiekolwiek dotychczas opublikowane:

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

Powody, dla których jest to poprawa:

  • Bezpieczny z wiodącymi zerami (w przeciwieństwie do BigInteger) i z ujemnymi wartościami bajtów (w przeciwieństwie do Byte.parseByte)

  • Nie konwertuje ciągu na char[]ani nie tworzy obiektów StringBuilder i String dla każdego bajtu.

  • Brak zależności bibliotek, które mogą być niedostępne

Dodawaj sprawdzanie argumentów za pomocą assertwyjątków, jeśli argument nie jest bezpieczny.

Dave L.
źródło
2
Czy możesz podać przykład, który został nieprawidłowo zdekodowany, lub wyjaśnić, dlaczego jest nieprawidłowy?
Dave L.
5
Nie działa dla ciągu „0”.
Zgłasza wyjątek
49
„0” jest nieprawidłowym wejściem. Każda bajt wymaga dwóch cyfr szesnastkowych. Jak zauważa odpowiedź: „Możesz dodać sprawdzanie argumentów ... jeśli argument nie jest bezpieczny”.
Dave L.
12
javax.xml.bind.DatatypeConverter.parseHexBinary (hexString) wydaje się być o około 20% szybszy niż powyższe rozwiązanie w moich mikrotestach (niezależnie od tego, ile są warte), a także poprawnie zgłasza wyjątki od nieprawidłowych danych wejściowych (np. „gg” nie jest poprawnym hexString, ale zwróci -77 używając zaproponowanego rozwiązania).
Trevor Freeman,
6
@DaedalusAlpha To zależy od twojego kontekstu, ale zwykle uważam, że lepiej jest zawieść szybko i głośno z takimi rzeczami, abyś mógł naprawić swoje założenia, zamiast cicho zwracać nieprawidłowe dane.
Dave L.
331

Jednowarstwowe:

import javax.xml.bind.DatatypeConverter;

public static String toHexString(byte[] array) {
    return DatatypeConverter.printHexBinary(array);
}

public static byte[] toByteArray(String s) {
    return DatatypeConverter.parseHexBinary(s);
}

Ostrzeżenia :

  • w Java 9 Jigsaw nie jest to już częścią (domyślnego) zestawu root java.se, więc spowoduje wyjątek ClassNotFoundException, chyba że podasz --add-moduły java.se.ee (dzięki @ eckes)
  • Niedostępne na Androidzie (dzięki Fabianza odnotowanie tego), ale możesz po prostu wziąć kod źródłowy, jeśli javax.xmlz jakiegoś powodu brakuje systemu . Dzięki @ Bert Regelinkza wyodrębnienie źródła.
Vladislav Rastrusny
źródło
14
IMHO powinna to być zaakceptowana / najwyższa odpowiedź, ponieważ jest krótka i przejrzysta (w przeciwieństwie do odpowiedzi @ DaveL) i nie wymaga żadnych zewnętrznych bibliotek (jak odpowiedź skaffmana). Również <Wprowadź zużyty dowcip o ponownym wynalezieniu roweru> .
Priidu Neemre
9
klasa datatypeconverter nie jest dostępna na przykład w Androidzie.
Fabian
4
Ostrzeżenie: w Java 9 Jigsaw nie jest to już część (domyślnego) java.sezestawu root, więc spowoduje, ClassNotFoundExceptionże nie zostanie określony--add-modules java.se.ee
eckes
2
@dantebarba Myślę, że javax.xml.bind.DatatypeConverterjuż zapewnia metodę kodowania / dekodowania danych Base64. Zobacz parseBase64Binary()i printBase64Binary().
DragShot
2
Aby dodać do problemów z tym DataTypeConverter, Java SE 11 całkowicie usunęła JAXB API i jest teraz dołączona tylko do Java EE. Możesz również dodać go jako zależność od Maven, jak sugerowano tutaj: stackoverflow.com/a/43574427/7347751
David Mordigal
79

Klasa Hex we wspólnym kodzie powinna to zrobić.

http://commons.apache.org/codec/

import org.apache.commons.codec.binary.Hex;
...
byte[] decoded = Hex.decodeHex("00A0BF");
// 0x00 0xA0 0xBF
skaffman
źródło
6
To też wygląda dobrze. Patrz org.apache.commons.codec.binary.Hex.decodeHex ()
Dave L.
To było ciekawe. Ale trudno mi było znaleźć ich rozwiązanie. Czy ma jakieś zalety w stosunku do tego, co zaproponowałeś (inne niż sprawdzanie parzystej liczby znaków)?
rafraf
38

Możesz teraz użyć BaseEncoding w guavacelu osiągnięcia tego.

BaseEncoding.base16().decode(string);

Aby to odwrócić, użyj

BaseEncoding.base16().encode(bytes);
jontro
źródło
27

Właściwie myślę, że BigInteger jest rozwiązaniem, które jest bardzo miłe:

new BigInteger("00A0BF", 16).toByteArray();

Edycja: Nie jest bezpieczne dla zer wiodących , jak zauważa plakat.

Dave L.
źródło
Tak też początkowo myślałem. I dziękuję za udokumentowanie tego - myślałem tylko, że powinienem ... zrobiłem kilka dziwnych rzeczy, których tak naprawdę nie rozumiałem - na przykład pominięcie jakiegoś wiodącego 0x00, a także pomieszanie rzędu 1 bajtu w ciągu 156 bajtów I bawił się.
rafraf
2
To dobra uwaga na temat wiodących zer. Nie jestem pewien, czy wierzę, że może pomieszać kolejność bajtów, i byłbym bardzo zainteresowany, aby to pokazać.
Dave L.
1
tak, jak tylko to powiedziałem, też mi nie uwierzyłem :) Uruchomiłem porównanie tablicy bajtów z BigInteger z mmyers'fromHexString i (bez 0x00) przeciwko obrażającemu ciągowi - były identyczne. Wystąpiło „pomieszanie”, ale mogło to być coś innego. Jutro przyjrzę się bliżej
rafraf
3
Problem z BigInteger polega na tym, że musi istnieć „bit znaku”. Jeśli bajt wiodący ma ustawiony wysoki bit, wynikowa tablica bajtów ma dodatkowe 0 w 1. pozycji. Ale nadal +1.
Gray
25

Jednowarstwowe:

import javax.xml.bind.DatatypeConverter;

public static String toHexString(byte[] array) {
    return DatatypeConverter.printHexBinary(array);
}

public static byte[] toByteArray(String s) {
    return DatatypeConverter.parseHexBinary(s);
}

Dla tych, którzy są zainteresowani faktycznym kodem za One- liners z FractalizeR (potrzebowałem tego, ponieważ javax.xml.bind nie jest dostępny dla Androida (domyślnie)), pochodzi on z com.sun.xml.internal.bind. DatatypeConverterImpl.java :

public byte[] parseHexBinary(String s) {
    final int len = s.length();

    // "111" is not a valid hex encoding.
    if( len%2 != 0 )
        throw new IllegalArgumentException("hexBinary needs to be even-length: "+s);

    byte[] out = new byte[len/2];

    for( int i=0; i<len; i+=2 ) {
        int h = hexToBin(s.charAt(i  ));
        int l = hexToBin(s.charAt(i+1));
        if( h==-1 || l==-1 )
            throw new IllegalArgumentException("contains illegal character for hexBinary: "+s);

        out[i/2] = (byte)(h*16+l);
    }

    return out;
}

private static int hexToBin( 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 char[] hexCode = "0123456789ABCDEF".toCharArray();

public String printHexBinary(byte[] data) {
    StringBuilder r = new StringBuilder(data.length*2);
    for ( byte b : data) {
        r.append(hexCode[(b >> 4) & 0xF]);
        r.append(hexCode[(b & 0xF)]);
    }
    return r.toString();
}
Bert Regelink
źródło
3
DatatypeConverter nie jest również domyślnie dostępny w Javie 9. Niebezpieczną rzeczą jest to, że kod, który go użyje, skompiluje się w Javie 1.8 lub wcześniejszej (Java 9 z ustawieniami źródła do wcześniejszych), ale otrzyma wyjątek czasu wykonywania w Javie 9 bez „--add-modules java.se.ee”.
Stephen M. - strajk -
24

HexBinaryAdapterUmożliwia Marszałek i unmarshal pomiędzy Stringi byte[].

import javax.xml.bind.annotation.adapters.HexBinaryAdapter;

public byte[] hexToBytes(String hexString) {
     HexBinaryAdapter adapter = new HexBinaryAdapter();
     byte[] bytes = adapter.unmarshal(hexString);
     return bytes;
}

To tylko przykład, który wpisałem ... Właściwie używam go takim, jaki jest i nie muszę tworzyć osobnej metody korzystania z niego.

GrkEngineer
źródło
5
Działa tylko wtedy, gdy ciąg wejściowy (hexString) ma parzystą liczbę znaków. W przeciwnym razie: wyjątek w wątku „main” java.lang.IllegalArgumentException: hexBinary musi mieć parzystą długość:
ovdsrn
3
Och, dzięki za zwrócenie na to uwagi. Użytkownik naprawdę nie powinien mieć nieparzystej liczby znaków, ponieważ tablica bajtów jest reprezentowana jako {0x00,0xA0,0xBf}. Każdy bajt ma dwie cyfry szesnastkowe lub skrypty. Tak więc każda liczba bajtów powinna zawsze mieć parzystą liczbę znaków. Dzięki, że o tym wspomniałeś.
GrkEngineer
8
Możesz użyć java.xml.bind.DatatypeConverter.parseHexBinary (hexString) bezpośrednio zamiast HexBinaryAdapter (który z kolei wywołuje DatatypeConverter). W ten sposób nie musisz tworzyć obiektu instancji adaptera (ponieważ metody DatatypeConverter są statyczne).
Trevor Freeman,
javax.xml.bind. * nie jest już dostępny w Javie 9. Niebezpieczną rzeczą jest to, że kod przy jego użyciu będzie się kompilował pod Javą 1.8 lub wcześniejszą wersją (Java 9 z ustawieniami źródła wcześniejszymi), ale otrzyma wyjątek czasu wykonywania działający pod Javą 9.
Stephen M. - strajk -
15

Oto metoda, która faktycznie działa (na podstawie kilku wcześniejszych częściowo poprawnych odpowiedzi):

private static byte[] fromHexString(final String encoded) {
    if ((encoded.length() % 2) != 0)
        throw new IllegalArgumentException("Input string must contain an even number of characters");

    final byte result[] = new byte[encoded.length()/2];
    final char enc[] = encoded.toCharArray();
    for (int i = 0; i < enc.length; i += 2) {
        StringBuilder curr = new StringBuilder(2);
        curr.append(enc[i]).append(enc[i + 1]);
        result[i/2] = (byte) Integer.parseInt(curr.toString(), 16);
    }
    return result;
}

Jedynym możliwym problemem, jaki widzę, jest to, czy łańcuch wejściowy jest wyjątkowo długi; wywołanie metody toCharArray () tworzy kopię wewnętrznej tablicy łańcucha.

EDYCJA: Aha, a przy okazji, bajty są podpisywane w Javie, więc ciąg wejściowy konwertuje na [0, -96, -65] zamiast [0, 160, 191]. Ale pewnie już to wiesz.

Michael Myers
źródło
1
Dzięki Michael - ratujesz życie! Praca nad projektem BlackBerry i próba konwersji reprezentacji ciągu bajtu z powrotem na bajt ... przy użyciu metody RIM „Byte.parseByte (byteString, 16)”. Przechowywane wyrzucanie NumberFormatExcpetion. Spędziłem godziny próbując dowiedzieć się, dlaczego. Twoja sugestia „Integer.praseInt ()” załatwiła sprawę. Dzięki jeszcze raz!!
BonanzaDriver
12

W Androidzie, jeśli pracujesz z hexem , możesz wypróbować okio .

proste użycie:

byte[] bytes = ByteString.decodeHex("c000060000").toByteArray();

i wynik będzie

[-64, 0, 6, 0, 0]
Miao1007
źródło
Testowałem wiele różnych metod, ale ta jest co najmniej dwa razy szybsza!
poby
5

BigInteger()Metoda z java.math jest bardzo powolny i nie recommandable.

Integer.parseInt(HEXString, 16)

może powodować problemy z niektórymi postaciami bez konwersji na cyfrę / liczbę całkowitą

dobrze działająca metoda:

Integer.decode("0xXX") .byteValue()

Funkcjonować:

public static byte[] HexStringToByteArray(String s) {
    byte data[] = new byte[s.length()/2];
    for(int i=0;i < s.length();i+=2) {
        data[i/2] = (Integer.decode("0x"+s.charAt(i)+s.charAt(i+1))).byteValue();
    }
    return data;
}

Miłej zabawy, powodzenia

Snajper
źródło
4

EDYCJA: jak wskazał @mmyers, ta metoda nie działa na danych wejściowych zawierających podciągi odpowiadające bajtom z ustawionym wysokim bitem („80” - „FF”). Wyjaśnienie znajduje się pod adresem Bug ID: 6259307 Byte.parseByte nie działa zgodnie z reklamą w dokumentacji SDK .

public static final byte[] fromHexString(final String s) {
    byte[] arr = new byte[s.length()/2];
    for ( int start = 0; start < s.length(); start += 2 )
    {
        String thisByte = s.substring(start, start+2);
        arr[start/2] = Byte.parseByte(thisByte, 16);
    }
    return arr;
}
Blair Conrad
źródło
1
Zamknij, ale ta metoda kończy się niepowodzeniem na danych wejściowych „00A0BBF”. Zobacz bugs.sun.com/bugdatabase/view_bug.do?bug_id=6259307 .
Michael Myers
1
Co dziwne, nie dotyczy to „9C”
rafraf
1
@mmyers: whoa. To nie jest dobrze. Przepraszam za zamieszanie. @ravigad: 9C ma ten sam problem, ponieważ w tym przypadku ustawiony jest wysoki bit.
Blair Conrad,
(bajt) Short.parseShort (thisByte, 16) rozwiązuje ten problem
Jamey Hicks
3

Oto, co warto, oto kolejna wersja, która obsługuje łańcuchy o nieparzystej długości, bez uciekania się do łączenia łańcuchów.

public static byte[] hexStringToByteArray(String input) {
    int len = input.length();

    if (len == 0) {
        return new byte[] {};
    }

    byte[] data;
    int startIdx;
    if (len % 2 != 0) {
        data = new byte[(len / 2) + 1];
        data[0] = (byte) Character.digit(input.charAt(0), 16);
        startIdx = 1;
    } else {
        data = new byte[len / 2];
        startIdx = 0;
    }

    for (int i = startIdx; i < len; i += 2) {
        data[(i + 1) / 2] = (byte) ((Character.digit(input.charAt(i), 16) << 4)
                + Character.digit(input.charAt(i+1), 16));
    }
    return data;
}
Conor Svensson
źródło
2

Zawsze stosowałem taką metodę

public static final byte[] fromHexString(final String s) {
    String[] v = s.split(" ");
    byte[] arr = new byte[v.length];
    int i = 0;
    for(String val: v) {
        arr[i++] =  Integer.decode("0x" + val).byteValue();

    }
    return arr;
}

ta metoda dzieli wartości szesnastkowe rozdzielane spacjami, ale nie byłoby trudno zmusić ją do podzielenia łańcucha według innych kryteriów, na przykład na grupy dwóch znaków.

pfranza
źródło
Łączenie łańcuchów nie jest konieczne. Wystarczy użyć Integer.valueOf (val, 16).
Michael Myers
Próbowałem już wcześniej używać takich konwersji konwersji i miałem mieszane wyniki
pfranza
dzięki - dziwnie działa dobrze z tym ciągiem: „9C001C” lub „001C21” i kończy się niepowodzeniem z tym: „9C001C21„ Wyjątek w wątku „głównym” java.lang.NumberFormatException: Dla ciągu wejściowego: „9C001C21” w java.lang. NumberFormatException.forInputString (nieznane źródło)
rafraf
(To nie jest bardziej dziwne niż w przypadku Byte/ byte: najwyższy zestaw bitów bez wiodącego -)
greybeard
2

Lubię rozwiązanie Character.digit, ale oto jak je rozwiązałem

public byte[] hex2ByteArray( String hexString ) {
    String hexVal = "0123456789ABCDEF";
    byte[] out = new byte[hexString.length() / 2];

    int n = hexString.length();

    for( int i = 0; i < n; i += 2 ) {
        //make a bit representation in an int of the hex value 
        int hn = hexVal.indexOf( hexString.charAt( i ) );
        int ln = hexVal.indexOf( hexString.charAt( i + 1 ) );

        //now just shift the high order nibble and add them together
        out[i/2] = (byte)( ( hn << 4 ) | ln );
    }

    return out;
}
Kernel Panic
źródło
2

Kod przedstawiony przez Bert Regelink po prostu nie działa. Spróbuj wykonać następujące czynności:

import javax.xml.bind.DatatypeConverter;
import java.io.*;

public class Test
{  
    @Test
    public void testObjectStreams( ) throws IOException, ClassNotFoundException
    {     
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);

            String stringTest = "TEST";
            oos.writeObject( stringTest );

            oos.close();
            baos.close();

            byte[] bytes = baos.toByteArray();
            String hexString = DatatypeConverter.printHexBinary( bytes);
            byte[] reconvertedBytes = DatatypeConverter.parseHexBinary(hexString);

            assertArrayEquals( bytes, reconvertedBytes );

            ByteArrayInputStream bais = new ByteArrayInputStream(reconvertedBytes);
            ObjectInputStream ois = new ObjectInputStream(bais);

            String readString = (String) ois.readObject();

            assertEquals( stringTest, readString);
        }
    }
Sean Coffey
źródło
2
To naprawdę inny problem i prawdopodobnie należy do innego wątku.
Sean Coffey,
1

Znalazłem Kernel Panic, który ma najbardziej przydatne rozwiązanie, ale napotkałem problemy, jeśli ciąg szesnastkowy był liczbą nieparzystą. rozwiązał to w ten sposób:

boolean isOdd(int value)
{
    return (value & 0x01) !=0;
}

private int hexToByte(byte[] out, int value)
{
    String hexVal = "0123456789ABCDEF"; 
    String hexValL = "0123456789abcdef";
    String st = Integer.toHexString(value);
    int len = st.length();
    if (isOdd(len))
        {
        len+=1; // need length to be an even number.
        st = ("0" + st);  // make it an even number of chars
        }
    out[0]=(byte)(len/2);
    for (int i =0;i<len;i+=2)
    {
        int hh = hexVal.indexOf(st.charAt(i));
            if (hh == -1)  hh = hexValL.indexOf(st.charAt(i));
        int lh = hexVal.indexOf(st.charAt(i+1));
            if (lh == -1)  lh = hexValL.indexOf(st.charAt(i+1));
        out[(i/2)+1] = (byte)((hh << 4)|lh);
    }
    return (len/2)+1;
}

Dodaję do tablicy pewną liczbę liczb szesnastkowych, więc przekazuję odwołanie do tablicy, której używam, i int potrzebuję przekonwertować i zwracam względną pozycję następnego numeru szesnastkowego. Zatem końcowa tablica bajtów ma [0] liczbę par szesnastkowych, [1 ...] par szesnastkowych, a następnie liczbę par ...

Clayton Balabanov
źródło
1

W oparciu o głosowane op, następujące elementy powinny być nieco bardziej wydajne:

  public static byte [] hexStringToByteArray (final String s) {
    if (s == null || (s.length () % 2) == 1)
      throw new IllegalArgumentException ();
    final char [] chars = s.toCharArray ();
    final int len = chars.length;
    final byte [] data = new byte [len / 2];
    for (int i = 0; i < len; i += 2) {
      data[i / 2] = (byte) ((Character.digit (chars[i], 16) << 4) + Character.digit (chars[i + 1], 16));
    }
    return data;
  }

Ponieważ: wstępna konwersja do tablicy char oszczędza sprawdzanie długości w charAt

Philip Helger
źródło
1

Jeśli preferujesz strumienie Java 8 jako swój styl kodowania, możesz to osiągnąć, używając tylko prymitywów JDK.

String hex = "0001027f80fdfeff";

byte[] converted = IntStream.range(0, hex.length() / 2)
    .map(i -> Character.digit(hex.charAt(i * 2), 16) << 4 | Character.digit(hex.charAt((i * 2) + 1), 16))
    .collect(ByteArrayOutputStream::new,
             ByteArrayOutputStream::write,
             (s1, s2) -> s1.write(s2.toByteArray(), 0, s2.size()))
    .toByteArray();

Te , 0, s2.size()parametry w funkcji kolektor złączyć można pominąć, jeśli nie przeszkadza wzrok IOException.

Andy Brown
źródło
0
public static byte[] hex2ba(String sHex) throws Hex2baException {
    if (1==sHex.length()%2) {
        throw(new Hex2baException("Hex string need even number of chars"));
    }

    byte[] ba = new byte[sHex.length()/2];
    for (int i=0;i<sHex.length()/2;i++) {
        ba[i] = (Integer.decode(
                "0x"+sHex.substring(i*2, (i+1)*2))).byteValue();
    }
    return ba;
}
David V.
źródło
0

Moje formalne rozwiązanie:

/**
 * Decodes a hexadecimally encoded binary string.
 * <p>
 * Note that this function does <em>NOT</em> convert a hexadecimal number to a
 * binary number.
 *
 * @param hex Hexadecimal representation of data.
 * @return The byte[] representation of the given data.
 * @throws NumberFormatException If the hexadecimal input string is of odd
 * length or invalid hexadecimal string.
 */
public static byte[] hex2bin(String hex) throws NumberFormatException {
    if (hex.length() % 2 > 0) {
        throw new NumberFormatException("Hexadecimal input string must have an even length.");
    }
    byte[] r = new byte[hex.length() / 2];
    for (int i = hex.length(); i > 0;) {
        r[i / 2 - 1] = (byte) (digit(hex.charAt(--i)) | (digit(hex.charAt(--i)) << 4));
    }
    return r;
}

private static int digit(char ch) {
    int r = Character.digit(ch, 16);
    if (r < 0) {
        throw new NumberFormatException("Invalid hexadecimal string: " + ch);
    }
    return r;
}

Jest jak funkcja hex2bin () PHP ale w stylu Java.

Przykład:

String data = new String(hex2bin("6578616d706c65206865782064617461"));
// data value: "example hex data"
Daniel De León
źródło
0

Późno na imprezę, ale połączyłem powyższą odpowiedź DaveL z klasą z odwrotnym działaniem - na wypadek, gdyby to pomogło.

public final class HexString {
    private static final char[] digits = "0123456789ABCDEF".toCharArray();

    private HexString() {}

    public static final String fromBytes(final byte[] bytes) {
        final StringBuilder buf = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            buf.append(HexString.digits[(bytes[i] >> 4) & 0x0f]);
            buf.append(HexString.digits[bytes[i] & 0x0f]);
        }
        return buf.toString();
    }

    public static final byte[] toByteArray(final String hexString) {
        if ((hexString.length() % 2) != 0) {
            throw new IllegalArgumentException("Input string must contain an even number of characters");
        }
        final int len = hexString.length();
        final byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4)
                    + Character.digit(hexString.charAt(i + 1), 16));
        }
        return data;
    }
}

I klasa testowa JUnit:

public class TestHexString {

    @Test
    public void test() {
        String[] tests = {"0FA1056D73", "", "00", "0123456789ABCDEF", "FFFFFFFF"};

        for (int i = 0; i < tests.length; i++) {
            String in = tests[i];
            byte[] bytes = HexString.toByteArray(in);
            String out = HexString.fromBytes(bytes);
            System.out.println(in); //DEBUG
            System.out.println(out); //DEBUG
            Assert.assertEquals(in, out);

        }

    }

}
DrPhill
źródło
0

Wiem, że to bardzo stary wątek, ale nadal lubię dodawać mój grosz.

Jeśli naprawdę muszę zakodować prosty ciąg szesnastkowy do konwertera binarnego, chciałbym to zrobić w następujący sposób.

public static byte[] hexToBinary(String s){

  /*
   * skipped any input validation code
   */

  byte[] data = new byte[s.length()/2];

  for( int i=0, j=0; 
       i<s.length() && j<data.length; 
       i+=2, j++)
  {
     data[j] = (byte)Integer.parseInt(s.substring(i, i+2), 16);
  }

  return data;
}
Tygrys
źródło
0

Zdecydowanie nie najczystsze rozwiązanie. Ale to działa dla mnie i jest dobrze sformatowane:

private String createHexDump(byte[] msg, String description) {
    System.out.println();
    String result = "\n" + description;
    int currentIndex = 0;
    int printedIndex = 0;
    for(int i=0 ; i<msg.length ; i++){
        currentIndex++;
        if(i == 0){
            result += String.format("\n  %04x ", i);
        }
        if(i % 16 == 0 && i != 0){
            result += " | ";
            for(int j=(i-16) ; j<msg.length && j<i ; j++) {
                char characterToAdd = (char) msg[j];
                if (characterToAdd == '\n') {
                    characterToAdd = ' ';
                }
                result += characterToAdd;
                printedIndex++;
            }

            result += String.format("\n  %04x ", i);
        }

        result += String.format("%02x ", msg[i]);
    }

    if(currentIndex - printedIndex > 0){
        int leftOvers = currentIndex - printedIndex;
        for(int i=0 ; i<16-leftOvers ; i++){
            result += "   ";
        }

        result += " | ";

        for(int i=msg.length-leftOvers ; i<msg.length ; i++){
            char characterToAdd = (char) msg[i];
            if (characterToAdd == '\n') {
                characterToAdd = ' ';
            }
            result += characterToAdd;
        }
    }

    result += "\n";

    return result;
}

Wyjście:

  S -> C
    0000 0b 00 2e 06 4d 6f 72 69 74 7a 53 6f 6d 65 20 54  |  .Heyyy Some T
    0010 43 50 20 73 74 75 66 66 20 49 20 63 61 70 74 75  | CP stuff I captu
    0020 72 65 64 2e 2e 77 65 6c 6c 20 66 6f 72 6d 61 74  | red..well format
    0030 3f                                               | ?
Moritz Schmidt
źródło
-2

Myślę, że zrobię to za ciebie. Połączyłem go z podobną funkcją, która zwróciła dane jako ciąg znaków:

private static byte[] decode(String encoded) {
    byte result[] = new byte[encoded/2];
    char enc[] = encoded.toUpperCase().toCharArray();
    StringBuffer curr;
    for (int i = 0; i < enc.length; i += 2) {
        curr = new StringBuffer("");
        curr.append(String.valueOf(enc[i]));
        curr.append(String.valueOf(enc[i + 1]));
        result[i] = (byte) Integer.parseInt(curr.toString(), 16);
    }
    return result;
}
Bob King
źródło
Po pierwsze, nie trzeba konwertować łańcucha na wielkie litery. Po drugie, możliwe jest dołączanie znaków bezpośrednio do StringBuffer, co powinno być znacznie wydajniejsze.
Michael Myers
-2

Dla mnie to było rozwiązanie, HEX = „FF01”, a następnie podzielony na FF (255) i 01 (01)

private static byte[] BytesEncode(String encoded) {
    //System.out.println(encoded.length());
    byte result[] = new byte[encoded.length() / 2];
    char enc[] = encoded.toUpperCase().toCharArray();
    String curr = "";
    for (int i = 0; i < encoded.length(); i=i+2) {
        curr = encoded.substring(i,i+2);
        System.out.println(curr);
        if(i==0){
            result[i]=((byte) Integer.parseInt(curr, 16));
        }else{
            result[i/2]=((byte) Integer.parseInt(curr, 16));
        }

    }
    return result;
}
Alejandro
źródło
Na to pytanie udzielono odpowiedzi od dłuższego czasu i ma kilka dobrych alternatyw; niestety, twoja odpowiedź nie zapewnia w tym momencie żadnej znacznie poprawionej wartości.
rfornal