我有一个字节数组充满十六进制数字和打印它的简单方式是相当没有意义的,因为有许多不可打印的元素。我需要的是精确的十六进制形式:3a5f771c
当前回答
只是迭代所有字节,转换为十六进制字符串使用整数。toString(不幸的是,没有字节。toString(带基数形参)并将它们全部附加到StringBuilder中。
byte[] arr;//set it to your value
StringBuilder sb=new StringBuilder(arr.length*2);//1 byte...2 hex digits
for(int i=0;i<arr.length;i++){
sb.append(Integer.toString(arr[i],16));
}
String hexValue=sb.toString();
这类似于指针Null的答案,但它使用整数。toString而不是String。格式提高性能。
其他回答
如果您正在为python寻找一模一样的字节数组,我已经将这个Java实现转换为python。
class ByteArray:
@classmethod
def char(cls, args=[]):
cls.hexArray = "0123456789ABCDEF".encode('utf-16')
j = 0
length = (cls.hexArray)
if j < length:
v = j & 0xFF
hexChars = [None, None]
hexChars[j * 2] = str( cls.hexArray) + str(v)
hexChars[j * 2 + 1] = str(cls.hexArray) + str(v) + str(0x0F)
# Use if you want...
#hexChars.pop()
return str(hexChars)
array = ByteArray()
print array.char(args=[])
对于固定长度,我会使用这样的东西,比如哈希值:
md5sum = String.format("%032x", new BigInteger(1, md.digest()));
下面是一些常见的选项,从简单(一行程序)到复杂(庞大的库)。如果您对性能感兴趣,请参阅下面的微基准测试。
选项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
好的,有很多方法可以做到这一点,但如果你决定使用一个库,我建议在你的项目中看看是否已经在一个库中实现了一些东西,而这个库已经是你项目的一部分,然后再添加一个新的库来做到这一点。例如,如果你还没有
org.apache.commons.codec.binary.Hex
也许你有…
org.apache.xerces.impl.dv.util.HexBin
从这里的讨论,特别是这个答案,这是我目前使用的函数:
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);
}
推荐文章
- 如何在java中格式化持续时间?(如格式H:MM:SS)
- urlencoder .encode(字符串)已弃用,我应该使用什么代替?
- javax.transaction.Transactional vs . org.springframework.transaction.annotation.Transactional
- Java 8接口方法中不允许“同步”的原因是什么?
- 如何找到Java堆大小和内存使用(Linux)?
- 使用Enum实现单例(Java)
- RabbitMQ与通道和连接之间的关系
- buildSessionFactory()配置方法在Hibernate中已弃用?
- Spring MVC -如何获得所有的请求参数在一个地图在Spring控制器?
- 如何在Java中按两个字段排序?
- 文件之间的差异。路径中的分隔符和斜杠
- 在方法参数中使用NotNull注释
- Spring MVC中处理可选参数的@RequestParam
- Tomcat:如何查找正在运行的Tomcat版本?
- “java”、“javaw”和“javaws”之间有什么区别?