Konvertieren Sie eine String-Darstellung eines Hex-Dump auf ein Byte-Array mit Java?
Frage
Ich bin nach einer Möglichkeit, eine lange Reihe (von Dump) zu konvertieren, dass Hex-Werten in einen Byte-Array darstellt.
Ich hätte es nicht besser formuliert als die Person, die gebucht die gleiche Frage hier .
Aber es ursprünglich zu halten, ich werde Phrase es meine eigene Art und Weise: nehme ich einen String "00A0BF"
haben, die ich als die interpretierte würde wie
byte[] {0x00,0xA0,0xBf}
Was soll ich tun?
Ich bin ein Anfänger Java und endete mit BigInteger
und für führende Nullen hex gerade aus. Aber ich denke, es ist hässlich, und ich bin sicher, dass ich etwas einfach fehle.
Lösung
Hier ist eine Lösung, die ich denke, ist besser als alle bisher geschrieben:
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;
}
Die Gründe, warum es eine Verbesserung ist:
-
Sicher mit führenden Nullen (im Gegensatz zu BigInteger) und mit einem negativen Byte-Werten (im Gegensatz zu Byte.parseByte)
-
Ist die String in ein
char[]
nicht konvertieren oder Stringbuilder und String-Objekte für jedes einzelne Byte. erstellen
-
Keine Bibliothek Abhängigkeiten, die nicht zur Verfügung stehen
Sie können ferner ein Argument hinzufügen über assert
oder Ausnahmen zu überprüfen, ob das Argument nicht bekannt ist, sicher zu sein.
Andere Tipps
Einzeiler:
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);
}
Warnungen :
- in Java 9 Jigsaw ist dies nicht mehr Teil der (default) java.se Wurzel
so eingestellt, dass es in einem ClassNotFoundException führen wird, wenn Sie angeben
--add-Module java.se.ee (dank @
eckes
) - Nicht verfügbar für Android (dank
Fabian
für Feststellung, dass), aber man kann einfach href="https://stackoverflow.com/a/11139098/173677"> nehmen, wenn Ihr System fehltjavax.xml
aus irgendeinem Grund. Dank @Bert Regelink
für die Quelle zu extrahieren.
Die Hex-Klasse in commons-codec sollte das für Sie tun.
http://commons.apache.org/codec/
import org.apache.commons.codec.binary.Hex;
...
byte[] decoded = Hex.decodeHex("00A0BF");
// 0x00 0xA0 0xBF
Die HexBinaryAdapter
bietet die Möglichkeit und Abstellungs zwischen String
und byte[]
Marschall.
import javax.xml.bind.annotation.adapters.HexBinaryAdapter;
public byte[] hexToBytes(String hexString) {
HexBinaryAdapter adapter = new HexBinaryAdapter();
byte[] bytes = adapter.unmarshal(hexString);
return bytes;
}
Das ist nur ein Beispiel, das ich in getippt ... Ich kann es eigentlich nur verwenden, wie und benötigen keine separaten Verfahren zur Verwendung zu machen.
Einzeiler:
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); }
Für diejenigen von Interesse in dem eigentlichen Code hinter dem Einzeiler Fractalizer (ich brauchte, dass seit javax.xml.bind für Android nicht verfügbar ist (Standardeinstellung)), das kommt von 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();
}
Hier ist ein Verfahren, das tatsächlich funktioniert (basierend auf mehreren vorangegangenen Halb richtigen Antworten):
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;
}
Das einzige mögliche Problem, dass ich sehen kann, ist, wenn die Eingabezeichenfolge extrem lang ist; Aufruf ToCharArray (), um eine Kopie des Strings internen Array macht.
EDIT: Oh, und übrigens, werden Bytes in Java unterzeichnet, so dass Ihr Eingabestring wandelt auf [0, -96, -65] anstelle von [0, 160, 191]. Aber Sie wussten wahrscheinlich, dass bereits.
In android, wenn Sie mit hex arbeiten, können Sie versuchen, Okio .
einfache Nutzung:
byte[] bytes = ByteString.decodeHex("c000060000").toByteArray();
und Ergebnis wird sein,
[-64, 0, 6, 0, 0]
EDIT: wie durch @mmyers erwähnt, funktioniert diese Methode nicht auf Eingabe arbeiten, die Teilstrings entsprechen Bytes mit dem hohen Bit gesetzt ( „80“ - „FF“) enthält. Die Erklärung dafür ist unter Fehler-ID: 6259307 Byte.parseByte nicht wie angekündigt im SDK arbeiten Dokumentation .
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;
}
Die BigInteger()
Methode von java.math ist sehr langsam und nicht recommandable.
Integer.parseInt(HEXString, 16)
kann Probleme mit einigen Zeichen verursachen, ohne Umwandlung in Digit / Integer
eine gut funktionierende Methode:
Integer.decode("0xXX") .byteValue()
Funktion:
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;
}
Haben Sie Spaß, viel Glück
Der Kodex von Bert Regelink präsentiert einfach nicht funktioniert. Versuchen Sie wie folgt vor:
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);
}
}
Für das, was es wert ist, hier ist eine andere Version, die Saiten ungerade Länge unterstützt, ohne auf die String-Verkettung greifen zu müssen.
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;
}
Ich habe immer verwendet eine Methode wie
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;
}
Diese Methode teilt auf Platz Hex-Werten begrenzt, aber es wäre nicht schwer sein, um es den String zu machen aufgeteilt auf anderen Kriterien wie in Gruppen von zwei Zeichen.
Ich mag die Character.digit Lösung, aber hier ist, wie ich es gelöst
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;
}
fand ich Kernel Panic die Lösung nützlichsten mir haben, aber lief in Probleme, wenn die Hex-String eine ungerade Zahl ist. löste es auf diese Weise:
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;
}
ich das Hinzufügen einer Anzahl von Hexadezimalzahlen zu einem Array, so dass ich passieren die Referenz auf das Array ich verwende, und die Int Ich brauche umgewandelt, und die relative Position des nächsten Hexadezimalzahl zurückkehrt. Also das letzte Byte-Array [0] Anzahl von Hex-Paare, [1 ...] hex Paare, dann die Anzahl der Paare ...
Auf der Grundlage der op stimmten Lösung sollte Folgendes etwas effizienter sein:
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;
}
Denn: die anfängliche Umwandlung in ein char-Array die Länge Kontrollen in charAt Ersatzteile
Wenn Sie eine Vorliebe für Java 8-Streams als Codierstil dann kann dies mit nur JDK Primitiven erreicht werden.
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();
Die , 0, s2.size()
Parameter in der Funktion Kollektor verketten kann entfallen, wenn Sie fangen IOException
nichts dagegen haben.
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;
}
Meine formale Lösung:
/**
* 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;
}
ist wie der PHP HEX2BIN () Funktion aber in Java-Stil.
Beispiel:
String data = new String(hex2bin("6578616d706c65206865782064617461"));
// data value: "example hex data"
Bei weitem nicht die sauberste Lösung. Aber es funktioniert für mich und ist gut formatiert:
private String createHexDump(byte[] msg, String description) {
System.out.println();
String result = "\n" + description;
int currentIndex = 0;
for(int i=0 ; i<msg.length ; i++){
currentIndex++;
if(i == 0){
result += String.format("\n %04x ", i);
}
if(i % 16 == 0 && i != 0){
result += " | ";
for(int j=(i-16) ; j<msg.length && j<i ; j++) {
char characterToAdd = (char) msg[j];
if (characterToAdd == '\n') {
characterToAdd = ' ';
}
result += characterToAdd;
}
result += String.format("\n %04x ", i);
}
result += String.format("%02x ", msg[i]);
}
if(currentIndex % 16 != 0){
int fitIns = msg.length / 16;
int leftOvers = msg.length - (fitIns * 16);
for(int i=0 ; i<16-leftOvers ; i++){
result += " ";
}
result += " | ";
for(int i=msg.length-leftOvers ; i<msg.length ; i++){
char characterToAdd = (char) msg[i];
if (characterToAdd == '\n') {
characterToAdd = ' ';
}
result += characterToAdd;
}
}
result += "\n";
return result;
}
Der Ausgang:
S -> C
0000 0b 00 2e 06 4d 6f 72 69 74 7a 53 6f 6d 65 20 54 | .Heyyy Some T
0010 43 50 20 73 74 75 66 66 20 49 20 63 61 70 74 75 | CP stuff I captu
0020 72 65 64 2e 2e 77 65 6c 6c 20 66 6f 72 6d 61 74 | red..well format
0030 3f | ?
spät zur Party, aber ich habe die Antwort oben DAVEL in eine Klasse mit der Rückwärts Aktion amalgamiert - nur für den Fall, es hilft.
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;
}
}
Und JUnit-Testklasse:
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);
}
}
}
Ich weiß, dass dies ein sehr alter Thread ist, aber immer noch gerne meinen Cent wert hinzuzufügen.
Wenn ich wirklich einen einfachen Hex-String zu Binärumformer muß Code, ich mag es tun, wie folge.
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;
}
ich denke, es wird für Sie tun. Ich gepflasterten zusammen aus einer ähnlichen Funktion, die die Daten als String zurückgegeben:
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;
}
Für mich war dies die Lösung, HEX = "FF01" dann FF aufgeteilt (255) und 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;
}