使用 HexFormat 來格式化和解析十六進制字符串
十六進制(Hexadecimal)是一種數(shù)制系統(tǒng),它使用 16 個數(shù)字來表示數(shù)值,分別是 0 到 9 和 A 到 F。
十六進制經(jīng)常用于表示字節(jié)數(shù)據(jù)。在十六進制表示中,一個字節(jié)可以用兩個十六進制數(shù)字表示。例如,字節(jié)的取值范圍是 0 到 255,可以用 00 到 FF 來表示。其中,00 表示二進制的 00000000,F(xiàn)F 表示二進制的 11111111。這在 Socket 通信協(xié)議的定義中很常見。
簡單來說,對于一些較短的二進制數(shù)據(jù),可以把它序列化為十六進制字符串,其中每 2 個字符,表示一個字節(jié)。同樣,也可以把十六進制的字符串解析為字節(jié)數(shù)組。最常見的場景就是把 Hash 計算的結(jié)果表示為十六進制字符串。
通常我們可以選擇使用第三方的 commons-codec 庫來實現(xiàn)格式化和解析十六進制字符串??赡苁沁@個功能需求太常見,于是從JDK 17 開始,標準庫中提供了一個 HexFormat 工具類,用于格式化和解析十六進制字符串。
簡單地編碼和解碼
簡單地把字節(jié)數(shù)組編碼為十六進制字符串,以及把十六進制字符串解析為字節(jié)數(shù)組。
package cn.springdoc.demo.test;
import java.util.HexFormat;
public class Main {
public static void main(String[] args) throws Exception {
HexFormat format = HexFormat.of();
String hex = format.formatHex("hello springdoc.cn".getBytes());
System.out.println("Hex=" + hex);
byte[] bytes = format.parseHex(hex);
System.out.println("bytes=" + new String(bytes));
}
}
首先,通過 of 靜態(tài)方法創(chuàng)建 HexFormat 實例對象。然后調(diào)用 formatHex 方法來把字節(jié)數(shù)組格式化十六進制字符串。最后再調(diào)用 parseHex 方法把十六進制字符串解析為字節(jié)數(shù)組。
parseHex 和 parseHex 都有一些重載方法,可以指定字符串或者字節(jié)數(shù)組的區(qū)間:
- String formatHex(byte[] bytes)
- String formatHex(byte[] bytes, int fromIndex, int toIndex)
- <A extends Appendable> A formatHex(A out, byte[] bytes)
- <A extends Appendable> A formatHex(A out, byte[] bytes, int fromIndex, int toIndex)
- byte[] parseHex(CharSequence string)
- byte[] parseHex(CharSequence string, int fromIndex, int toIndex)
- byte[] parseHex(char[] chars, int fromIndex, int toIndex)
執(zhí)行方法,輸出如下:
Hex=68656c6c6f20737072696e67646f632e636e
bytes=hello springdoc.cn
分隔符
在一些場景中,給十六進制字符串中每一個字節(jié)之間添加一個分隔符可讀性會更好。
例如:68:65:6c:6c:6f:20:73:70:72:69:6e:67:64:6f:63:2e:63:6e。
// 通過 ofDelimiter 方法創(chuàng)建HexFormat,指定分隔符
HexFormat format = HexFormat.ofDelimiter(":");
String hex = format.formatHex("hello springdoc.cn".getBytes());
System.out.println("Hex=" + hex);
byte[] bytes = format.parseHex(hex);
System.out.println("bytes=" + new String(bytes));
// 獲取分隔符
String delimiter = format.delimiter();
System.out.println("分隔符=" + delimiter);
只需要通過 ofDelimiter 靜態(tài)方法,指定分隔符來創(chuàng)建 HexFormat 實例即可,同時也可以通過 delimiter 方法來獲取設置的分隔符。
輸出如下,每個字節(jié)(兩個字符)之間都添加了指定的分隔符:
Hex=68:65:6c:6c:6f:20:73:70:72:69:6e:67:64:6f:63:2e:63:6e
bytes=hello springdoc.cn
分隔符=:
前綴和后綴
也可以給每個字節(jié),即每兩個十六進制字符串設置前綴和后綴。
HexFormat format = HexFormat.ofDelimiter(":")
.withPrefix("[") // 設置前綴
.withSuffix("]") // 設置后綴
;
String hex = format.formatHex("hello springdoc.cn".getBytes());
System.out.println("Hex=" + hex);
byte[] bytes = format.parseHex(hex);
System.out.println("bytes=" + new String(bytes));
System.out.println("前綴=" + format.prefix() + ", 后綴=" + format.suffix());
通過 withPrefix 和 withSuffix 方法來設置前綴和后綴。注意 HexFormat 是不可變的對象(類似于 String),所以任何修改都會返回一個新的 HexFormat 對象。
輸出如下:
Hex=[68]:[65]:[6c]:[6c]:[6f]:[20]:[73]:[70]:[72]:[69]:[6e]:[67]:[64]:[6f]:[63]:[2e]:[63]:[6e]
bytes=hello springdoc.cn
前綴=[, 后綴=]
大小寫
十六進制中有 A - F 字母,也可以設置字母的大小寫。
HexFormat format = HexFormat.of()
// .withLowerCase() // 字母小寫,默認
.withUpperCase() // 字母大寫
;
String hex = format.formatHex("hello springdoc.cn".getBytes());
System.out.println("Hex=" + hex);
byte[] bytes = format.parseHex(hex);
System.out.println("bytes=" + new String(bytes));
System.out.println("大寫=" + format.isUpperCase());
通過 withLowerCase(默認)和 withUpperCase 方法來設置十六進制字符串中字母的大小寫,通過 isUpperCase 方法來獲取是否開啟了大寫。
輸出如下:
Hex=68656C6C6F20737072696E67646F632E636E
bytes=hello springdoc.cn
大寫=true
實際案例
最后來看一個實際案例,把 SHA256 哈希值編碼為十六進制字符串:
package cn.springdoc.demo.test;
import java.security.MessageDigest;
import java.util.HexFormat;
public class Main {
public static void main(String[] args) throws Exception {
// 創(chuàng)建 SHA256 MessageDigest
MessageDigest digest = MessageDigest.getInstance("SHA256");
// 計算字符串 "123456" 的哈希值
byte[] sha256 = digest.digest("123456".getBytes());
// 把哈希結(jié)果編碼為十六進制字符串
String sha256Hex = HexFormat.of().withUpperCase().formatHex(sha256);
System.out.println(sha256Hex);
}
}
輸出如下:
8D969EEF6ECAD3C29A3A629280E686CF0C3F5D5A86AFF3CA12020C923ADC6C92
總結(jié)
本文介紹了如何使用 JDK 17 新增的 HexFormat 工具類來格式化和解析十六進制字符串,通過 HexFormat 工具類還可以輕松地設置分隔符,字母大小寫以及前綴和后綴。