使用Java将hex转储的string表示forms转换为字节数组?
我正在寻找一种方法来转换长string(从转储),表示hex值到一个字节数组。
我不能比在这里发表同样的问题的人更好的expression:
http://www.experts-exchange.com/Programming/Programming_Languages/Java/Q_21062554.html
但是为了保持原来的格式,我会用自己的方式来描述它:假设我有一个string“00A0BF”,我想将其解释为字节[] {0x00,0xA0,0xBf},我该怎么办?
我是一个Java新手,最终使用BigInteger并注意领先的hex零。 但我认为这是丑陋的,我相信我错过了一些简单的事情…
这里有一个解决scheme,我认为比任何发布到目前为止:
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[]
,或者为每个字节创buildStringBuilder和String对象。
如果参数不被认为是安全的,可以自由地通过assert
或exception来添加参数检查。
单行:
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
注意到这一点),但是如果您的系统由于某种原因缺lessjavax.xml
,您可以采取源代码 。 感谢@Bert Regelink
提取源代码。
commons-codec中的Hex类应该为你做。
你现在可以在guava
使用BaseEncoding来实现这一点。
BaseEncoding.base16().decode(string);
扭转它使用
BaseEncoding.base16().encode(bytes);
HexBinaryAdapter
提供了对String
和byte[]
进行编组HexBinaryAdapter
的能力。
import javax.xml.bind.annotation.adapters.HexBinaryAdapter; public byte[] hexToBytes(String hexString) { HexBinaryAdapter adapter = new HexBinaryAdapter(); byte[] bytes = adapter.unmarshal(hexString); return bytes; }
这只是我input的一个例子…我实际上只是使用它,不需要做一个单独的方法来使用它。
其实我觉得BigInteger是很好的解决scheme:
new BigInteger("00A0BF", 16).toByteArray();
编辑: 不正确的引导零 ,如海报所述。
单行:
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的One-liners背后的实际代码感兴趣的人 (我需要这样做 ,因为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(); }
这是一个实际工作的方法(基于几个以前的半正确答案):
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; }
我能看到的唯一可能的问题是,如果inputstring非常长; 调用toCharArray()使string的内部数组的副本。
编辑:哦,顺便说一下,字节是用Java签名的,所以你的inputstring转换为[0,-96,-65]而不是[0,160,191]。 但是你可能已经知道了。
在android中,如果你正在使用hex,你可以尝试okio 。
简单的用法:
byte[] bytes = ByteString.decodeHex("c000060000").toByteArray();
结果将是
[-64, 0, 6, 0, 0]
编辑:如@ mmyers指出的,这种方法不适用于包含高位设置(“80” – “FF”)的字节对应的子string的input。 解释在Bug ID:6259307 Byte.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; }
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); } }
我一直使用像
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; }
这个方法在空格分隔的hex值上进行分割,但是在任何其他标准(比如两个字符的分组)上分割string并不难。
我喜欢Character.digit解决scheme,但是我是这样解决的
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; }
对于什么是值得的,这里是另一个支持奇数长度的string的版本,而不诉诸string连接。
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; }
java.math中的BigInteger()
方法非常慢,不可重新指定。
Integer.parseInt(HEXString, 16)
可能会导致一些字符的问题,而不会转换为数字/整数
一口井工作方式:
Integer.decode("0xXX") .byteValue()
function:
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; }
玩得开心,祝你好运
我发现内核恐慌有解决scheme对我最有用的,但遇到问题,如果hexstring是一个奇数。 这样解决了:
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; }
我正在给数组添加一些hex数字,所以我把引用传递给我正在使用的数组,而我需要转换并返回下一个hex数字的相对位置。 所以最后的字节数组有[十]hex数对,[1 …]hex对,然后对的数量…
根据op投票的解决scheme,以下应该是更有效率:
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; }
因为:初始转换为字符数组可以省去charAt中的长度检查
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; }
我的正式解决scheme
/** * 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"
我认为会为你做。 我用一个类似的函数把它们拼凑在一起,把数据作为一个string返回:
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; }
对我来说,这是解决scheme,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; }