我有一个字节数组充满十六进制数字和打印它的简单方式是相当没有意义的,因为有许多不可打印的元素。我需要的是精确的十六进制形式:3a5f771c
当前回答
我通常使用以下方法进行debuf语句,但我不知道这是否是最好的方法
private static String digits = "0123456789abcdef";
public static String toHex(byte[] data){
StringBuffer buf = new StringBuffer();
for (int i = 0; i != data.length; i++)
{
int v = data[i] & 0xff;
buf.append(digits.charAt(v >> 4));
buf.append(digits.charAt(v & 0xf));
}
return buf.toString();
}
其他回答
这个怎么样?
String byteToHex(final byte[] hash)
{
Formatter formatter = new Formatter();
for (byte b : hash)
{
formatter.format("%02x", b);
}
String result = formatter.toString();
formatter.close();
return result;
}
HexFormat是在Java 17中加入的:
String hex = HexFormat.of().formatHex(array);
我的解决方案是基于maybeWeCouldStealAVan的解决方案,但不依赖于任何额外分配的查找表。它不使用任何“int-to-char”类型强制转换(实际上,Character.forDigit()做到了这一点,执行一些比较来检查数字的真实情况),因此可能会稍慢一些。请随意在任何你想用的地方使用。欢呼。
public static String bytesToHex(final byte[] bytes)
{
final int numBytes = bytes.length;
final char[] container = new char[numBytes * 2];
for (int i = 0; i < numBytes; i++)
{
final int b = bytes[i] & 0xFF;
container[i * 2] = Character.forDigit(b >>> 4, 0x10);
container[i * 2 + 1] = Character.forDigit(b & 0xF, 0x10);
}
return new String(container);
}
下面是一些常见的选项,从简单(一行程序)到复杂(庞大的库)。如果您对性能感兴趣,请参阅下面的微基准测试。
选项1:代码片段-简单(仅使用JDK/Android)
选项1a: BigInteger
一个非常简单的解决方案是使用BigInteger的十六进制表示:
new BigInteger(1, someByteArray).toString(16);
注意,因为它处理的数字不是任意字节字符串,它将省略前导零——这可能是也可能不是你想要的(例如,3字节输入的000AE3 vs 0AE3)。这也非常慢,大约比选项2慢100倍。
选项1b: String.format()
使用%X占位符,String.format()能够将大多数基本类型(short, int, long)编码为十六进制:
String.format("%X", ByteBuffer.wrap(eightByteArray).getLong());
选项1c:整数/长(只有4/8字节数组)
如果你只有4个字节的数组,你可以使用Integer类的toHexString方法:
Integer.toHexString(ByteBuffer.wrap(fourByteArray).getInt());
这同样适用于8字节数组和Long
Long.toHexString(ByteBuffer.wrap(eightByteArray).getLong());
选项1d: JDK17+ HexFormat
最后,JDK 17通过HexFormat提供了直接的十六进制编码的一级支持:
HexFormat hex = HexFormat.of();
hex.formatHex(someByteArray)
选项2:代码片段-高级
这是一个完整的功能,复制和粘贴代码片段,支持大写/小写和小写。它经过优化以最小化内存复杂性和最大化性能,并且应该与所有现代Java版本(5+)兼容。
private static final char[] LOOKUP_TABLE_LOWER = new char[]{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66};
private static final char[] LOOKUP_TABLE_UPPER = new char[]{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46};
public static String encode(byte[] byteArray, boolean upperCase, ByteOrder byteOrder) {
// our output size will be exactly 2x byte-array length
final char[] buffer = new char[byteArray.length * 2];
// choose lower or uppercase lookup table
final char[] lookup = upperCase ? LOOKUP_TABLE_UPPER : LOOKUP_TABLE_LOWER;
int index;
for (int i = 0; i < byteArray.length; i++) {
// for little endian we count from last to first
index = (byteOrder == ByteOrder.BIG_ENDIAN) ? i : byteArray.length - i - 1;
// extract the upper 4 bit and look up char (0-A)
buffer[i << 1] = lookup[(byteArray[index] >> 4) & 0xF];
// extract the lower 4 bit and look up char (0-A)
buffer[(i << 1) + 1] = lookup[(byteArray[index] & 0xF)];
}
return new String(buffer);
}
public static String encode(byte[] byteArray) {
return encode(byteArray, false, ByteOrder.BIG_ENDIAN);
}
完整的源代码与Apache v2许可证和解码器可以在这里找到。
选项3:使用一个小型优化库:bytes-java
在从事上一个项目时,我创建了这个用于在Java中使用字节的小工具包。它没有外部依赖关系,并且与Java 7+兼容。它包括,除其他外,一个非常快速和经过良好测试的HEX en/解码器:
import at.favre.lib.bytes.Bytes;
...
Bytes.wrap(someByteArray).encodeHex()
你可以在Github上查看:bytes-java。
选项4:Apache Commons Codec
当然也有好的通用编解码器。(警告意见)当我在上面概述的项目中工作时,我分析了代码,非常失望;大量重复的无组织代码,过时的和外来的编解码器可能只对极少数有用,而且非常过度设计和缓慢的流行编解码器实现(特别是Base64)。因此,如果你想使用它或其他选择,我会做出明智的决定。无论如何,如果你仍然想使用它,这里有一个代码片段:
import org.apache.commons.codec.binary.Hex;
...
Hex.encodeHexString(someByteArray));
选项5:谷歌番石榴
通常情况下,您已经将番石榴作为依赖项。如果是这样,就用:
import com.google.common.io.BaseEncoding;
...
BaseEncoding.base16().lowerCase().encode(someByteArray);
选项6:Spring Security
如果你使用Spring框架和Spring Security,你可以使用以下方法:
import org.springframework.security.crypto.codec.Hex
...
new String(Hex.encode(someByteArray));
选择7:充气城堡
如果你已经使用了安全框架Bouncy Castle,你可以使用它的Hex util:
import org.bouncycastle.util.encoders.Hex;
...
Hex.toHexString(someByteArray);
不是真的选项8:Java 9+兼容性或“不使用jaxb javax/xml/bind/DatatypeConverter”
在以前的Java(8及以下)版本中,JAXB的Java代码是作为运行时依赖项包含的。由于Java 9和Jigsaw模块化,如果没有显式声明,你的代码不能访问模块之外的其他代码。所以要注意,如果你得到一个异常:
java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException
在Java 9+的JVM上运行时。如果是这样,那么将实现切换到上面的任何替代方案。再看看这个问题。
微基准测试
下面是对不同大小的字节数组进行编码的简单JMH微基准测试的结果。这些值是每秒操作数,所以越高越好。 请注意,微观基准测试通常并不代表现实世界的行为,所以对这些结果持保留态度。
| Name (ops/s) | 16 byte | 32 byte | 128 byte | 0.95 MB |
|----------------------|-----------:|-----------:|----------:|--------:|
| Opt1: BigInteger | 2,088,514 | 1,008,357 | 133,665 | 4 |
| Opt2/3: Bytes Lib | 20,423,170 | 16,049,841 | 6,685,522 | 825 |
| Opt4: Apache Commons | 17,503,857 | 12,382,018 | 4,319,898 | 529 |
| Opt5: Guava | 10,177,925 | 6,937,833 | 2,094,658 | 257 |
| Opt6: Spring | 18,704,986 | 13,643,374 | 4,904,805 | 601 |
| Opt7: BC | 7,501,666 | 3,674,422 | 1,077,236 | 152 |
| Opt8: JAX-B | 13,497,736 | 8,312,834 | 2,590,940 | 346 |
规格:JDK 8u202, i7-7700K, Win10, 24GB Ram。点击这里查看完整的基准测试。
基准更新2022
下面是使用当前的JMH 1.35、Java 17和更高端的计算机的结果
| Name (ops/s) | 16 byte | 32 byte | 128 byte | 0.95 MB |
|----------------------|-----------:|-----------:|----------:|--------:|
| Opt1: BigInteger | 2,941,403 | 1,389,448 | 242,096 | 5 |
| Opt2/3: Bytes Lib | 31,724,981 | 22,786,906 | 6,197,028 | 930 |
规格:JDK temurin 17.0.4, Ryzen 5900X, Win11, 24GB DDR4 Ram
从这里的讨论,特别是这个答案,这是我目前使用的函数:
private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars);
}
My own tiny benchmarks (a million bytes a thousand times, 256 bytes 10 million times) showed it to be much faster than any other alternative, about half the time on long arrays. Compared to the answer I took it from, switching to bitwise ops --- as suggested in the discussion --- cut about 20% off of the time for long arrays. (Edit: When I say it's faster than the alternatives, I mean the alternative code offered in the discussions. Performance is equivalent to Commons Codec, which uses very similar code.)
2k20版本,相对于Java 9的压缩字符串:
private static final byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII);
public static String bytesToHex(byte[] bytes) {
byte[] hexChars = new byte[bytes.length * 2];
for (int j = 0; j < bytes.length; j++) {
int v = bytes[j] & 0xFF;
hexChars[j * 2] = HEX_ARRAY[v >>> 4];
hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
}
return new String(hexChars, StandardCharsets.UTF_8);
}
推荐文章
- codestyle;把javadoc放在注释之前还是之后?
- 如何在Spring中定义List bean ?
- 将Set<T>转换为List<T>的最简洁的方法
- 在JavaScript中,什么相当于Java的Thread.sleep() ?
- 使用Java重命名文件
- URL从Java中的类路径加载资源
- .toArray(new MyClass[0]) or .toArray(new MyClass[myList.size()])?
- Hibernate中不同的保存方法之间有什么区别?
- Java 8流和数组操作
- Java Regex捕获组
- Openssl不被视为内部或外部命令
- 如何添加自定义方法到Spring Data JPA
- 如何在Ubuntu中设置Java环境路径
- 无法执行dex:在Eclipse中超过GC开销限制
- 有人能解释一下JPA和Hibernate中的mappedBy吗?