首页 文章

使用Java将十六进制转储的字符串表示形式转换为字节数组?

提问于
浏览 919 次
314

我正在寻找一种方法来转换长字符串(从转储),它表示十六进制值到一个字节数组 .

我不能比发布the same question here的人更好地措辞 .

但为了保持原创,我会用自己的方式来表达它:假设我有一个字符串 "00A0BF" ,我想把它解释为

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

我该怎么办?

我是Java新手,最后使用 BigInteger 并注意领先的十六进制零 . 但我觉得它很难看,我确信我错过了一些简单的东西 .

25 回答

  • 310

    对于它的 Value ,这是另一个支持奇数长度字符串的版本,而不需要求助于字符串连接 .

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

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

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

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

    编辑: Not safe for leading zeros ,如海报所述 .

  • 14

    我知道这是一个非常古老的线程,但仍然喜欢添加我的便士 Value .

    如果我真的需要将简单的十六进制字符串编码为二进制转换器,我想按如下方式进行 .

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

    commons-codec中的Hex类应该为你做 .

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

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

    到目前为止还不是最干净的解决方案 . 但它适用于我,格式很好:

    private String createHexDump(byte[] msg, String description) {
        System.out.println();
        String result = "\n" + description;
        int currentIndex = 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;
                }
    
                result += String.format("\n  %04x ", i);
            }
    
            result += String.format("%02x ", msg[i]);
        }
    
        if(currentIndex % 16 != 0){
            int fitIns = msg.length / 16;
            int leftOvers = msg.length - (fitIns * 16);
            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;
    }
    

    输出:

    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                                               | ?
    
  • 3

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

    这只是我输入的一个例子......我实际上只是按原样使用它,不需要单独使用它 .

  • 2

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

    BaseEncoding.base16().decode(string);
    

    扭转它的使用

    BaseEncoding.base16().encode(bytes);
    
  • 0

    我总是使用像这样的方法

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

    此方法在空格分隔的十六进制值上进行拆分,但要将字符串拆分为任何其他条件(如两个字符的分组)并不困难 .

  • 22

    One-liners: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背后的实际代码感兴趣的人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();
    }
    
  • 3

    java.math中的 BigInteger() 方法非常慢,无法推荐 .

    Integer.parseInt(HEXString, 16)

    某些字符可能会导致问题,而无需转换为数字/整数

    一个好的工作方法:

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

    玩得开心,祝你好运

  • 3

    我的正式解决方案

    /**
     * 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() Function而是Java风格 .

    Example:

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

    One-liners:

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

    Warnings

    _999_在Java 9 Jigsaw中这不再是(默认)java.se根集的一部分,因此它将导致ClassNotFoundException,除非你指定--add-modules java.se.ee(感谢@ eckes

    • 在Android上不可用(感谢 Fabian 注意到这一点),但如果您的系统由于某种原因缺少 javax.xml ,则可以take the source code . 感谢@ Bert Regelink 提取源代码 .
  • 66

    迟到了,但是我把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);
    
            }
    
        }
    
    }
    
  • 2

    这是一种实际工作的方法(基于以前的几个半正确答案):

    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] . 但你可能已经知道了 .

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

    这是一个我认为比目前发布的任何一个更好的解决方案:

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

    这是一个改进的原因:

    • 带前导零的安全(与BigInteger不同)和负字节值(与Byte.parseByte不同)

    • 不将String转换为 char[] ,或为每个字节创建StringBuilder和String对象 .

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

    如果不知道参数是否安全,可以通过 assert 或异常添加参数检查 .

  • -2

    编辑:正如@mmyers所指出的,此方法不适用于包含与高位设置的字节对应的子串的输入("80" - "FF") . 解释是在Bug ID: 6259307 Byte.parseByte not working as advertised in the SDK Documentation .

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

    我喜欢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;
    }
    
  • 554

    对我来说这是解决方案,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;
    }
    
  • -2

    在android中,如果你正在使用十六进制,你可以尝试okio .

    简单用法:

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

    结果将是

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

    我发现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 ...]十六进制对,然后对数...

  • -1

    基于操作选择的解决方案,以下应该更有效:

    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中的长度检查失效

  • 0

    如果您偏好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();
    

    如果您不介意捕获 IOException ,则可以省略收集器连接函数中的 , 0, s2.size() 参数 .

相关问题