16 進値を表す長い文字列 (ダンプから) をバイト配列に変換する方法を探しています。
ここに同じ質問を投稿した人よりもうまく表現できなかったでしょう。
しかし、オリジナルを保つために、私なりの言い方をします"00A0BF"
。
byte[] {0x00,0xA0,0xBf}
私は何をすべきか?
私はJavaの初心者でありBigInteger
、先頭の16進ゼロを使用して注意することになりました。しかし、それは醜いと思います。単純なものが欠けていると確信しています。
更新 (2021) - Java 17には以下が含まれるようになりjava.util.HexFormat
ました (わずか 25 年かかりました):
HexFormat.of().parseHex(s)
これまでに投稿されたどのソリューションよりも優れていると思うソリューションは次のとおりです。
/* 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[]
1 バイトごとに StringBuilder および String オブジェクトを作成したりしません。
利用できない可能性のあるライブラリの依存関係はありません
assert
引数が安全であることがわかっていない場合は、または例外を介して自由に引数チェックを追加してください。
commons-codec の Hex クラスがそれを行う必要があります。
http://commons.apache.org/codec/
import org.apache.commons.codec.binary.Hex;
...
byte[] decoded = Hex.decodeHex("00A0BF");
// 0x00 0xA0 0xBF
これを実現するためにBaseEncodingを使用できるようにguava
なりました。
BaseEncoding.base16().decode(string);
それを逆にするには
BaseEncoding.base16().encode(bytes);
実際、BigInteger is ソリューションは非常に優れていると思います。
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();
}
は、 との間でHexBinaryAdapter
マーシャリングおよびアンマーシャリングを行う機能を提供します。String
byte[]
import javax.xml.bind.annotation.adapters.HexBinaryAdapter;
public byte[] hexToBytes(String hexString) {
HexBinaryAdapter adapter = new HexBinaryAdapter();
byte[] bytes = adapter.unmarshal(hexString);
return bytes;
}
これは私が入力した単なる例です...実際にはそのまま使用するだけで、それを使用するための別の方法を作成する必要はありません。
これは実際に機能する方法です(以前のいくつかの半正解に基づいています):
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, 160, 191] ではなく [0, -96, -65] に変換されます。しかし、おそらくあなたはすでにそれを知っていました。
BigInteger()
java.mathのメソッドは非常に遅く、お勧めできません。
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;
}
楽しんでください、頑張ってください
EDIT:@mmyersが指摘したように、このメソッドは、上位ビットが設定されたバイト(「80」-「FF」)に対応する部分文字列を含む入力では機能しません。説明はBug ID: 6259307 Byte.parseByte not working as Advized in the SDK Documentation にあります。
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;
}
価値があるのは、文字列連結に頼らずに奇数長の文字列をサポートする別のバージョンです。
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;
}
私はCharacter.digitソリューションが好きですが、これが私がそれを解決した方法です
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;
}
私はいつも次のような方法を使用してきました
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;
}
このメソッドは、スペースで区切られた 16 進値で分割しますが、2 文字のグループ化など、他の基準で文字列を分割することは難しくありません。
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);
}
}
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 の長さのチェックが省略されます。
Kernel Panic が最も役立つ解決策であることがわかりましたが、16 進文字列が奇数の場合に問題が発生しました。このように解決しました:
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;
}
多数の 16 進数を配列に追加しているので、使用している配列への参照を渡し、必要な int を変換して、次の 16 進数の相対位置を返します。したがって、最終的なバイト配列には [0] 個の 16 進数のペア、[1...] 個の 16 進数のペア、そしてペアの数が含まれます...
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;
}
私はあなたのためにそれをすると思います。データを文字列として返す同様の関数からそれをまとめました。
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;
}