我正在寻找一种方法来将表示十六进制值的长字符串(从转储中)转换为字节数组。

我的措辞比发布相同问题的人更好在这里。

但是要保持其原始性,我将以自己的方式来表达:假设我有一个字符串"00A0BF",我想将其解释为

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


我应该怎么做?

我是Java新手,最终使用BigInteger并注意前导十六进制零。但是我认为这很丑陋,并且我肯定我缺少一些简单的东西。

评论

另请参见stackoverflow.com/questions/9655181/…。

我在这里驯服了BigInteger。

FWIW String.getBytes()不能像您认为的那样工作。不得不学习这个困难的方法。如果(“ FF” .getBytes()!=“ ff” .getBytes()){System.out.println(“再试一次”); }

#1 楼

我认为以下解决方案比迄今为止发布的解决方案都要好:并且具有负字节值(与Byte.parseByte不同)


不将String转换为char[],也不为每个单个字节创建StringBuilder和String对象。


没有可能不可用的库依赖项


可以通过assert随意添加参数检查,或者如果未知该参数是安全的,则可以添加异常。

评论


您能否举一个解码不正确的示例,或解释它的错误之处?

–戴夫L。
2011-4-17 14:35



它不适用于字符串“ 0”。引发java.lang.StringIndexOutOfBoundsException

–ovdsrn
2011年6月8日在20:06

“ 0”是无效输入。字节每个都需要两个十六进制数字。正如答案所指出的,“可以随意添加参数检查...如果未知该参数是安全的。”

–戴夫L。
2011年6月9日在16:42

在我的微型测试中,javax.xml.bind.DatatypeConverter.parseHexBinary(hexString)似乎比上述解决方案快20%(无论它们值多少钱),并且可以在无效输入上正确抛出异常(例如“ gg”不是有效的hexString,但将使用建议的解决方案返回-77)。

–特雷弗·弗里曼
2012年4月4日在18:31

@DaedalusAlpha这取决于您的上下文,但是通常我发现最好还是大声地失败,以便您可以修正自己的假设,而不是静默地返回错误的数据。

–戴夫L。
15年6月17日在16:03

#2 楼

单行代码:

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


警告:Java 9拼图中的


这不再是(默认)java的一部分.se root
设置,因此将导致ClassNotFoundException,除非您指定
--add-modules java.se.ee(感谢@ eckes
在Android上不可用(感谢Fabian (请注意),但是如果您的系统由于某种原因缺少javax.xml,则可以直接获取源代码。感谢@ Bert Regelink提取了源文件。


评论


恕我直言,这应该是可接受的/最佳答案,因为它简短明了(与@DaveL的答案不同),并且不需要任何外部库(如skaffman的答案)。另外,<输入关于重新设计自行车的陈词滥调>。

– Priidu Neemre
15年8月18日在12:40



例如,datatypeconverter类在android中不可用。

–法比安
16 Feb 10'在12:41

警告:在Java 9 Jigsaw中,这不再是(默认)java.se根集的一部分,因此除非您指定--add-modules java.se.ee,否则它将导致ClassNotFoundException。

–eckes
16年10月10日在12:17

@dantebarba我认为javax.xml.bind.DatatypeConverter已经提供了一种用于对Base64数据进行编码/解码的方法。请参阅parseBase64Binary()和printBase64Binary()。

– DragShot
17年7月3日在22:18

为了解决DataTypeConverter的问题,Java SE 11完全删除了JAXB API,现在仅包含在Java EE中。您还可以按照以下建议将其添加为Maven依赖项:stackoverflow.com/a/43574427/7347751

– David Mordigal
19年1月30日,下午5:51

#3 楼

commons编解码器中的Hex类应该为您做到这一点。

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

import org.apache.commons.codec.binary.Hex;
...
byte[] decoded = Hex.decodeHex("00A0BF");
// 0x00 0xA0 0xBF


评论


这看起来也不错。参见org.apache.commons.codec.binary.Hex.decodeHex()

–戴夫L。
08-09-26 at 17:46

很有意思。但是我发现他们的解决方案很难遵循。它对您的建议有什么好处(除了检查偶数个字符以外)?

– rafraf
08-9-27 at 1:06

#4 楼

现在,您可以在guava中使用BaseEncoding来完成此操作。

BaseEncoding.base16().decode(string);


要反向操作,请使用

BaseEncoding.base16().encode(bytes);


#5 楼

实际上,我认为BigInteger的解决方案非常好:

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


编辑:如海报所指出的那样,对于前导零并不安全。

评论


一开始我也这么认为。谢谢您记录下来-我只是想我应该...它做了一些奇怪的事情,尽管我不是很了解-例如,省略了一些前导0x00,并且在156字节的字符串中混合了1字节的顺序在玩。

– rafraf
08-9-26在16:43

关于领先0是一个好点。我不确定我是否相信它会混淆字节顺序,并且对它的演示很有兴趣。

–戴夫L。
08-9-26 at 16:55

是的,我一说完,我也不相信:)我将BigInteger的字节数组与mmyers'fromHexString进行了比较,并且对有问题的字符串(没有0x00)进行了比较-它们是相同的。确实发生了“混淆”,但可能还有其他原因。明天我会仔细看

– rafraf
08年9月26日在17:09

BigInteger的问题在于必须有一个“符号位”。如果前导字节设置了高位,则结果字节数组在第一个位置将有一个额外的0。但仍然+1。

–灰色
2011年10月28日在16:20

#6 楼


单行代码:

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



对于那些对FractalizeR单行代码背后的实际代码感兴趣的人(自从javax.xml.bind不适用于Android(默认情况下)),它来自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();
}


评论


默认情况下,DatatypeConverter在Java 9中也不可用。危险的是,使用它的代码将在Java 1.8或更早版本(源设置为更早的Java 9)下编译,但是在Java 9下会出现运行时异常,而没有“ --add-modules java.se.ee”。

–斯蒂芬M-罢工-
17年8月28日在16:54

#7 楼

HexBinaryAdapter提供了在Stringbyte[]之间进行封送和封送的功能。

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

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


这只是我输入的一个示例...实际上我只是按原样使用而不必无需使用其他方法。

评论


仅当输入字符串(hexString)的字符数为偶数时才有效。否则:线程“主”中的异常java.lang.IllegalArgumentException:hexBinary必须为偶数长度:

–ovdsrn
2011年6月8日在20:15



哦,谢谢你指出这一点。用户实际上不应该使用奇数个字符,因为字节数组表示为{0x00,0xA0,0xBf}。每个字节都有两个十六进制数字或半字节。因此,任意数量的字节应始终具有偶数个字符。感谢您提到这一点。

– GrkEngineer
2011年6月16日15:54

您可以直接使用java.xml.bind.DatatypeConverter.parseHexBinary(hexString),而不是使用HexBinaryAdapter(后者又称为DatatypeConverter)。这样,您不必创建适配器实例对象(因为DatatypeConverter方法是静态的)。

–特雷弗·弗里曼
2012年4月4日在18:33

javax.xml.bind。*在Java 9中不再可用。危险的事情是使用它的代码将在Java 1.8或更早版本(源设置为更早的Java 9)下编译,但会在Java 9下运行时发生运行时异常。

–斯蒂芬M-罢工-
17年8月28日在16:52

#8 楼

这是一种实际可行的方法(基于先前的一些半正确答案):

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


我唯一可能看到的问题是输入字符串是否极端长;调用toCharArray()会生成字符串内部数组的副本。

编辑:哦,顺便说一下,字节是用Java签名的,因此您的输入字符串将转换为[0,-96,-65 ],而不是[0,160,191]。但是您可能已经知道了。

评论


谢谢迈克尔-您是救生员!在BlackBerry项目上工作,并尝试使用RIM的“ Byte.parseByte(byteString,16)”方法将字节的字符串表示形式转换回字节。保持抛出NumberFormatExcpetion。花了几个小时才弄清楚原因。您对“ Integer.praseInt()”的建议可以解决问题。再次感谢!!

– BonanzaDriver
11-10-23在19:28

#9 楼

在android中,如果您使用的是十六进制,则可以尝试okio。

简单用法:

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


,结果将是

[-64, 0, 6, 0, 0]


评论


我已经测试了许多不同的方法,但是这种方法至少快两倍!

–poby
5月5日6:07

#10 楼

来自java.math的BigInteger()方法非常慢且不推荐。

Integer.parseInt(HEXString, 16)

可能会导致某些字符出现问题,而没有
转换为Digit / Integer

一个良好的工作方法:

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


功能:

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


玩得开心,很好运气

#11 楼

编辑:如@mmyers所指出,此方法不适用于包含子字符串的输入,该子字符串对应于设置了高位(“ 80”-“ FF”)的字节。说明是在Bug ID:6259307 Byte.parseByte不能按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;
}


评论


关闭,但是该方法在给定的输入“ 00A0BBF”上失败。请参阅bugs.sun.com/bugdatabase/view_bug.do?bug_id=6259307。

–Michael Myers♦
08-09-26 15:34

同样奇怪的是,它不处理“ 9C”

– rafraf
08年9月26日在15:58

@mmyers:哇。这不好。很抱歉给您带来混乱。 @ravigad:9C有同样的问题,因为在这种情况下,高位被置位。

–布莱尔·康拉德(Blair Conrad)
08-9-26在16:37

(byte)Short.parseShort(thisByte,16)解决了这个问题

–杰米·希克斯(Jamey Hicks)
17年7月28日在13:10

#12 楼

值得的是,这是另一个版本,它支持奇数长度的字符串,而无需求助于字符串连接。

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


#13 楼

我一直使用像

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


这样的方法,该方法在以空格分隔的十六进制值上进行拆分,但并不难于在任何其他条件下将其拆分为字符串例如分为两个字符的分组。

评论


字符串连接是不必要的。只需使用Integer.valueOf(val,16)。

–Michael Myers♦
08-09-26 15:21

我以前尝试过使用基数转换,但结果参差不齐

–pfranza
08-09-26 15:23

谢谢-奇怪的是,它可以在以下字符串上正常工作:“ 9C001C”或“ 001C21”,并在以下字符串中失败:“ 9C001C21”线程“ main”中的异常java.lang.NumberFormatException:对于输入字符串:java.lang中的“ 9C001C21”。 NumberFormatException.forInputString(未知来源)

– rafraf
08-09-26在16:07

(这在字节/字节的情况下并不奇怪:最高位设置为无前导-)

–灰胡子
16年2月13日在18:17

#14 楼

我喜欢Character.digit解决方案,但是这是我解决的方法

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


#15 楼

Bert Regelink提出的代码根本行不通。
请尝试以下操作:

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
2012年11月8日19:48

#16 楼

我发现Kernel Panic对我来说是最有用的解决方案,但是如果十六进制字符串是奇数,则会遇到问题。我是这样解决的:

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


我在数组中添加了一些十六进制数字,所以我将引用传递给了我正在使用的数组,并且将int需要转换并返回下一个十六进制数字的相对位置。因此,最后一个字节数组的数目为[0]个十六进制对,[1 ...]个十六进制对,然后为对数...

#17 楼

根据经投票表决的解决方案,以下代码应该更有效:

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


因为:初始转换为char数组可以节省charAt

#18 楼

如果您偏爱Java 8流作为编码样式,则可以仅使用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();


收集器连接功能中的, 0, s2.size()参数可以是如果您不介意捕捉IOException,则将其省略。

#19 楼

晚会晚了,但是我已经将DaveL的上述答案合并为带有反作用的类,以防万一它有所帮助。

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


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

        }

    }

}


#20 楼

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


#21 楼

我的正式解决方案:

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


类似于PHP hex2bin()函数,但采用Java风格。

示例:

String data = new String(hex2bin("6578616d706c65206865782064617461"));
// data value: "example hex data"


#22 楼

我知道这是一个非常老的线程,但是仍然想增加我的一分钱。

如果我真的需要编写一个简单的十六进制字符串到二进制转换器,我想这样做。

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


#23 楼

我想会帮你的。我通过类似的函数将其拼凑在一起,该函数以字符串形式返回了数据:

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


评论


首先,您不需要将字符串转换为大写。其次,可以将char直接附加到StringBuffer上,这应该效率更高。

–Michael Myers♦
08-09-26 15:48

#24 楼

对我来说这就是解决方案,HEX =“ FF01”然后拆分为FF(255)和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;
}


评论


这个问题已经回答了一段时间,并且有几种不错的选择。不幸的是,目前您的答案并没有提供任何明显改善的价值。

–rfornal
2014年12月6日,下午1:10