Java – 如何将字节数组转换为十六进制

本文向您展示了几种转换字节数组或byte[]转换为十六进制(基数为 16 或十六进制)字符串代表的方法。

  1. String.format
  2. Integer.toHexString
  3. Apache Commons 编解码器 – commons-codec
  4. Spring 安全加密 – spring-security-crypto
  5. 按位移位和屏蔽。(教育目的)

注意
Apache Commons-Codec 和 Spring Security Crypto 模块都使用类似的5. Bitwise shifting and masking技术将字节数组转换为十六进制字符串,请研究下面的源代码,它对教育目的很有用。

1. 字符串格式 %02x

String.format是将字节数组转换为十六进制的最简单和明显的方法,%02x对于小写十六进制,%02X大写十六进制。

ByteToHexExample1.java

package com.mkyong.crypto.bytes;

import java.nio.charset.StandardCharsets;

public class ByteToHexExample1 {

    public static String hex(byte[] bytes) {
        StringBuilder result = new StringBuilder();
        for (byte aByte : bytes) {
            result.append(String.format("%02x", aByte));
            // upper case
            // result.append(String.format("%02X", aByte));
        }
        return result.toString();
    }

    public static void main(String[] args) {

        String input = "a";
        System.out.println(hex(input.getBytes(StandardCharsets.UTF_8)));

    }
}

输出

终端

  61

2. Integer.toHexString

Integer.toHexString(int i)接受一个intas 参数并返回一个十六进制字符串。关键是将 转换byte为 anint并使用 a0xff进行掩码以防止符号扩展

ByteToHexExample2.java

package com.mkyong.crypto.bytes;

import java.nio.charset.StandardCharsets;

public class ByteToHexExample2 {

    public static String hex(byte[] bytes) {
        StringBuilder result = new StringBuilder();
        for (byte aByte : bytes) {
            int decimal = (int) aByte & 0xff;               // bytes widen to int, need mask, prevent sign extension
                                                            // get last 8 bits
            String hex = Integer.toHexString(decimal);
            if (hex.length() % 2 == 1) {                    // if half hex, pad with zero, e.g \t
                hex = "0" + hex;
            }
            result.append(hex);
        }
        return result.toString();
    }

    public static void main(String[] args) {

        String input = "a";
        System.out.println(hex(input.getBytes(StandardCharsets.UTF_8)));

    }
}

输出

终端

  61

3. Apache Commons 编解码器

我们可以使用Hex.encodeHex转换byte[]为十六进制字符串,或Hex.decodeHex将十六进制字符串转换为byte[].

ByteToHexExample3

package com.mkyong.crypto.bytes;

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

import java.nio.charset.StandardCharsets;

public class ByteToHexExample3 {

    public static String hex(byte[] bytes) {
        char[] result = Hex.encodeHex(bytes);
        return new String(result);
    }

    public static String unhex(String hex) throws DecoderException {
        return new String(Hex.decodeHex(hex));
    }

    public static void main(String[] args) throws DecoderException {

        String input = "a";

        String hex = hex(input.getBytes(StandardCharsets.UTF_8));
        System.out.println(hex);    // 61

        String unhex = unhex(hex);
        System.out.println(unhex);  // a

    }
}

马文。

pom.xml

  <dependency>
      <groupId>commons-codec</groupId>
      <artifactId>commons-codec</artifactId>
      <version>1.14</version>
  </dependency>

4. Spring Security 加密

在 Spring Security 中,我们可以使用Hex.encode转换byte[]为十六进制字符串。

ByteToHexExample4.java

package com.mkyong.crypto.bytes;

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

import java.nio.charset.StandardCharsets;

public class ByteToHexExample4 {

    public static void main(String[] args) {

        String input = "a";

        char[] encode = Hex.encode(input.getBytes(StandardCharsets.UTF_8));
        String hex = new String(encode);
        System.out.println(hex);                    // 61

        byte[] decode = Hex.decode(hex);
        System.out.println(new String(decode));     // a

    }
}

马文。

pom.xml

  <dependency>
      <groupId>org.springframework.security</groupId>
      <artifactId>spring-security-crypto</artifactId>
      <version>5.3.2.RELEASE</version>
  </dependency>

5. 按位移位和屏蔽。

下面的源代码来自Spring Security Crypto模块,Apache Commons Codes使用类似的技术将字节数组转换为十六进制字符串,只是变量名称或长度计算不同,核心思想相同。

5.1 十六进制编码。

十六进制

package org.springframework.security.crypto.codec;

  //...
  private static final char[] HEX = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
            'a', 'b', 'c', 'd', 'e', 'f' };

  public static char[] encode(byte[] bytes) {
    final int nBytes = bytes.length;
    char[] result = new char[2 * nBytes];         //  1 hex contains two chars
                                                  //  hex = [0-f][0-f], e.g 0f or ff

    int j = 0;
    for (byte aByte : bytes) {                    // loop byte by byte

                                                  // 0xF0 = FFFF 0000
      result[j++] = HEX[(0xF0 & aByte) >>> 4];    // get the top 4 bits, first half hex char

                                                  // 0x0F = 0000 FFFF
      result[j++] = HEX[(0x0F & aByte)];          // get the bottom 4 bits, second half hex char

                                                  // combine first and second half, we get a complete hex
    }

    return result;
  }

困难的部分是理解以下两个陈述。


  HEX[(0xF0 & aByte) >>> 4];

  HEX[(0x0F & aByte)];

5.1.1 HEX[(0xF0 & aByte) >>> 4](前半十六进制)

例如,一个字符a,二进制是0110 0001,在bitwise ANDa之后0xF0,它变成0110 0000


0110 0001           # 1 hex = 2 chars [0-f][0-f]
                    # In this case, hex = [0110][0001]

0110 0001           # 0110 = first half hex, 0001 = second half hex
&
FFFF 0000           # 0xF0 = FFFF 0000 , bitwise AND operator.

0110 0000           # result 0110 0000

逻辑右移4位0110 0000 >>> 4,就变成了0000 0110。阅读此Java >> 和 >>> 按位移位运算符


0110 0000 |

???? 0110 | 0000    # >>> 4
0000 0110 | 0000    # >>> 4 (logical right shift, zero extension)

0000 0110           # result, the first half hex

把这个二进制0000 0110转成十进制,就是a 6,看变量static final char[] HEX,索引6的值是6,十六进制的前半部分是6。

5.1.2 HEX[(0x0F & aByte)](后半十六进制)
同一个字符a,二进制是0110 0001bitwise AND0x0F


0110 0001           # 0110 = first half hex, 0001 = second half hex
&                   # bitwise AND operator.
0000 FFFF           # 0x0F = 0000 FFFF

0000 0001           # result 0000 0001

把这个二进制0000 00001转成十进制,就是1,再看变量static final char[] HEX,索引1的值是1,十六进制的后半部分是1。

我们将十六进制的前半部分和后半部分组合起来,即 6 + 1,它变成61。对于字符a,十六进制是61

5.2 十六进制解码。

十六进制

package org.springframework.security.crypto.codec;

  //...

    private static final char[] HEX = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
            'a', 'b', 'c', 'd', 'e', 'f' };

    public static byte[] decode(CharSequence s) {
        int nChars = s.length();

        if (nChars % 2 != 0) {                                             
            throw new IllegalArgumentException(
                    "Hex-encoded string must have an even number of characters");
        }

        byte[] result = new byte[nChars / 2];                                  // 1 hex = 2 char

        for (int i = 0; i < nChars; i += 2) {                                  // step 2, 1 hex = 2 char
            int msb = Character.digit(s.charAt(i), 16);                         // char -> hex, base16
            int lsb = Character.digit(s.charAt(i + 1), 16);

            if (msb < 0 || lsb < 0) {
                throw new IllegalArgumentException(
                    "Detected a Non-hex character at " + (i + 1) + " or " + (i + 2) + " position");
            }
            result[i / 2] = (byte) ((msb << 4) | lsb);
        }
        return result;
    }

}

例如,相同的 hex 61、 character a

Character.digit是 JDK API,将 a 转换char为基数 16 或十六进制并返回int.


int msb = Character.digit(s.charAt(i), 16);           // msb = 6
int lsb = Character.digit(s.charAt(i + 1), 16);       // lsb = 1

注意
msblsb变量名是有点不可思议,我觉得作者是指第4位和最后4位。

在 Java 中, int 6,二进制是0000 0110; 对于 int1二进制文件是0000 0001


(byte) ((msb << 4) | lsb);  // in this example, msb = 6, lsb = 1

# (byte) ((msb << 4) | lsb);

     | 0000 0000 | 0000 0000 | 0000 0000 | 0000 0110 |    # msb, 6 is an int, 32-bit
                                                          # (byte) ((msb << 4) | lsb);
                                                          # (byte) ((6 << 4) | 1);
# <<-- 4
0000 | 0000 0000 | 0000 0000 | 0000 0000 | 0110 ???? |    # 6 << 4

0000 | 0000 0000 | 0000 0000 | 0000 0000 | 0110 0000 |    # left shift, ignore sign, zero extension.

     | 0000 0000 | 0000 0000 | 0000 0000 | 0110 0000 |    # final msb

     # bitwise | operator, bitwise inclusive OR

     | 0000 0000 | 0000 0000 | 0000 0000 | 0000 0001 |    # lsb = 1

     | 0000 0000 | 0000 0000 | 0000 0000 | 0110 0001 |    # msb | lsb = 0110 0001

     | 0110 0001 |                                        # (byte) (msb|lsb) , down cast from int to byte 8-bit

最终的二进制文件是0110 0001.

此代码段将二进制字符串转换为字符串,对于 binary 0110 0001,字符串为a


  int charCode = Integer.parseInt("01100001", 2);
  System.out.println(charCode);                       // 97, look ascii table
  String str = Character.toString((char) charCode);
  System.out.println(str);                            // output = a

谢谢阅读。

 

  • 6
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值