转换十六进制转储到字节数组使用Java字符串表示?

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

我不可能比发布同样的问题的人更好地表达它。

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

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

我该怎么办?

我是一个Java新手,最终使用BigInteger并注意前导十六进制零。但我认为它很丑,我肯定我错过了一些简单的东西。

584471 次浏览

common -codec中的Hex类应该为您做到这一点。

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

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

我一直用的方法是

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

此方法根据空格分隔的十六进制值进行分割,但根据任何其他条件(例如分成两个字符的组)将字符串分割并不困难。

我想我会帮你的。我用一个类似的函数把它拼凑在一起,该函数以字符串形式返回数据:

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

编辑:正如@mmyers所指出的,此方法不适用于包含与高位设置的字节对应的子字符串的输入("80"——“FF")。解释在错误ID: 6259307字节。parseByte没有像SDK文档中宣传的那样工作

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

下面是一个实际有效的方法(基于之前几个半正确的答案):

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

实际上,我认为BigInteger是很好的解决方案:

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

编辑:前导0不安全,正如海报所指出的。

更新(2021年)- Java 17现在包括java.util.HexFormat(只花了25年):

HexFormat.of().parseHex(s)


对于较旧版本的Java:

这里有一个解决方案,我认为比目前为止发布的任何解决方案都要好:

/* s must be an even-length string. */
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或异常添加实参检查。

java中的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;
}

玩得开心,好运

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

我喜欢这个角色。数位解,我是这样解的

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

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

这只是我输入的一个例子……实际上,我只是按原样使用它,不需要为使用它创建一个单独的方法。

一行程序:

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

警告:

  • 在Java 9 Jigsaw这不再是(默认)Java的一部分。se根 设置,否则将导致ClassNotFoundException,除非您指定 ——add-modules java.se.ee(感谢@eckes)
  • Android上不可用(感谢Fabian指出这一点),但如果你的系统由于某种原因缺少javax.xml,你可以以源代码为例。感谢@Bert Regelink提取源代码。

我发现内核恐慌有解决方案对我最有用,但遇到问题,如果十六进制字符串是一个奇数。是这样解决的:

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…十六进制对,然后是对的数目…

一行程序:

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一行程序背后的实际代码感兴趣的人(我需要它,因为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();
}

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

你现在可以在guava中使用BaseEncoding来实现这一点。

BaseEncoding.base16().decode(string);

反过来使用

BaseEncoding.base16().encode(bytes);

基于op投票的解决方案,下面的方案应该更有效一些:

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

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

在android中,如果你正在使用hex,你可以尝试okio

简单的用法:

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

结果是

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

这里有另一个版本,它支持奇数长度的字符串,而不诉诸字符串连接。

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

我的正式解决方案:

/**
* 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()函数,但是是Java风格。

例子:

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

如果您偏好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()参数可以省略。

迟到了,但我已经把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);


}


}


}

我知道这是一个非常老的帖子,但仍然喜欢添加我的便士价值。

如果我真的需要编码一个简单的十六进制字符串到二进制转换器,我想这样做。

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

如果你的需要不仅仅是偶尔的转换,那么你可以使用HexUtils

例子:

byte[] byteArray = Hex.hexStrToBytes("00A0BF");

这是最简单的情况。您的输入可能包含分隔符(如MAC地址、证书指纹等),您的输入可能是流式的,等等。在这种情况下,更容易证明引入HexUtils这样的外部库,不管它有多小。

在JDK 17中,HexFormat类将满足大多数需求,对HexUtils之类的东西的需求大大减少。然而,HexUtils仍然可以用于一些事情,比如从十六进制(流)转换非常大的量或漂亮的十六进制打印(想想有线转储),这是JDK HexFormat类不能做的。

(完全披露:我是HexUtils的作者)