แปลงการแทนสตริงของดัมพ์ hex เป็นอาร์เรย์ไบต์โดยใช้ Java?


372

ฉันกำลังมองหาวิธีการแปลงสตริงยาว (จากการถ่ายโอนข้อมูล) ที่แสดงถึงค่าฐานสิบหกเป็นอาร์เรย์ไบต์

ผมไม่ได้เรียบเรียงได้ดีกว่าคนที่โพสต์คำถามเดียวกันที่นี่

แต่เพื่อให้เป็นต้นฉบับฉันจะพูดตามวิธีของตัวเอง: สมมติว่าฉันมีสตริง"00A0BF"ที่ฉันต้องการตีความว่าเป็น

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

ฉันควรทำอย่างไรดี?

ฉันเป็นสามเณร Java และจบลงด้วยการใช้BigIntegerและระวังเลขศูนย์เลขฐานสิบหก แต่ฉันคิดว่ามันน่าเกลียดและฉันแน่ใจว่าฉันขาดอะไรง่าย ๆ


ดูยังstackoverflow.com/questions/9655181/...
flow2k

ฉันได้ฝึกให้เชื่องที่นี่BigInteger
John McClane

FWIW String.getBytes()จะไม่ทำงานอย่างที่คุณคิด ต้องเรียนรู้วิธีนี้อย่างหนัก if ("FF".getBytes() != "ff".getBytes()) { System.out.println("Try again"); }
tir38

คำตอบ:


636

นี่เป็นวิธีแก้ปัญหาที่ฉันคิดว่าดีกว่าโพสต์อื่น ๆ :

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)

  • ไม่แปลงสตริงเป็น a char[]หรือสร้าง StringBuilder และวัตถุ String สำหรับทุกไบต์

  • ไม่มีการอ้างอิงไลบรารีที่อาจไม่พร้อมใช้งาน

อย่าลังเลที่จะเพิ่มการตรวจสอบข้อโต้แย้งผ่านassertหรือข้อยกเว้นถ้าไม่แน่ใจว่าข้อโต้แย้งนั้นปลอดภัย


2
คุณสามารถยกตัวอย่างที่ถอดรหัสไม่ถูกต้องหรืออธิบายว่ามันผิดหรือเปล่า?
Dave L.

5
มันใช้ไม่ได้กับสตริง "0" มันโยน java.lang.StringIndexOutOfBoundsException
ovdsrn

49
"0" ไม่ใช่อินพุตที่ถูกต้อง ไบต์ต้องการตัวเลขฐานสิบหกสองหลักแต่ละตัว ในฐานะที่เป็นคำตอบบันทึก "อย่าลังเลที่จะเพิ่มการตรวจสอบการโต้แย้ง ... ถ้าอาร์กิวเมนต์ไม่ปลอดภัย"
Dave L.

12
javax.xml.bind.DatatypeConverter.parseHexBinary (hexString) ดูเหมือนว่าจะเร็วกว่าโซลูชันข้างต้นประมาณ 20% ในการทดสอบขนาดเล็กของฉัน (สำหรับสิ่งเล็ก ๆ น้อย ๆ ที่พวกเขาควรค่า) เช่นเดียวกับการโยนข้อยกเว้นของอินพุตที่ไม่ถูกต้อง (เช่น "gg") ไม่ใช่ hexString ที่ถูกต้อง แต่จะส่งกลับ -77 โดยใช้โซลูชันตามที่เสนอ)
เทรเวอร์ฟรีแมน

6
@DaedalusAlpha มันขึ้นอยู่กับบริบทของคุณ แต่โดยปกติแล้วฉันพบว่ามันจะดีกว่าถ้าล้มเหลวอย่างรวดเร็วและดังในสิ่งต่าง ๆ เพื่อที่คุณจะสามารถแก้ไขข้อสมมติฐานของคุณได้แทนที่จะส่งคืนข้อมูลที่ไม่ถูกต้องแบบเงียบ ๆ
Dave L.

331

หนึ่งสมุทร:

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 นี้ไม่ได้เป็นส่วนหนึ่งของชุดราก java.se (เริ่มต้น) ดังนั้นมันจะส่งผลให้ ClassNotFoundException เว้นแต่ว่าคุณจะระบุ --add-modules java.se.ee (ขอบคุณ @ eckes)
  • ไม่สามารถใช้งานได้บน Android (ขอบคุณFabianสำหรับการสังเกต) แต่คุณสามารถนำซอร์สโค้ดได้หากระบบของคุณขาดjavax.xmlเหตุผลบางประการ ขอบคุณ @ Bert Regelinkสำหรับการแยกแหล่งที่มา

14
IMHO สิ่งนี้ควรเป็นคำตอบที่ได้รับการยอมรับ / ยอดนิยมเนื่องจากสั้นและสะอาด (ต่างจากคำตอบของ @ DaveL) และไม่ต้องการ libs ภายนอกใด ๆ (เช่นคำตอบของ skaffman) นอกจากนี้<Enter เป็นเรื่องตลกที่สวมใส่เกี่ยวกับการปฏิรูปจักรยาน>
Priidu Neemre

9
ชั้น datatypeconverter ไม่สามารถใช้ได้ใน Android เช่น
เฟเบียน

4
คำเตือน: ในชวา 9 จิ๊กซอว์นี้ไม่ได้เป็นส่วนหนึ่งของ (เริ่มต้น) java.seชุดรากจึงจะมีผลในClassNotFoundExceptionเว้นแต่คุณจะระบุ--add-modules java.se.ee
Eckes

2
@dantebarba ฉันคิดว่าjavax.xml.bind.DatatypeConverterมีวิธีการเข้ารหัส / ถอดรหัสข้อมูล Base64 แล้ว ดูและparseBase64Binary() printBase64Binary()
DragShot

2
เพื่อเพิ่มปัญหาด้วยDataTypeConverterJava SE 11 ได้ลบ JAXB API ออกทั้งหมดและตอนนี้มีอยู่ใน Java EE เท่านั้น คุณสามารถเพิ่มเป็นการพึ่งพา Maven ตามที่แนะนำได้ที่นี่: stackoverflow.com/a/43574427/7347751
David Mordigal

79

คลาส Hex ในตัวแปลงสัญญาณทั่วไปควรทำเพื่อคุณ

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

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

6
นี่ก็ดูดี ดู org.apache.commons.codec.binary.Hex.decodeHex ()
เดฟแอล

มันน่าสนใจ. แต่ฉันพบวิธีแก้ปัญหาของพวกเขายากที่จะทำตาม มันมีข้อดีกว่าสิ่งที่คุณเสนอหรือไม่ (นอกเหนือจากการตรวจสอบจำนวนตัวอักษร)
rafraf

38

ตอนนี้คุณสามารถใช้BaseEncodingในguavaการดำเนินการนี้

BaseEncoding.base16().decode(string);

เพื่อย้อนกลับใช้

BaseEncoding.base16().encode(bytes);

27

ที่จริงแล้วฉันคิดว่า BigInteger เป็นทางออกที่ดีมาก:

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

แก้ไข: ไม่ปลอดภัยสำหรับศูนย์นำหน้าตามที่ระบุไว้โดยผู้โพสต์


ฉันก็คิดอย่างนั้นในตอนแรก และขอบคุณสำหรับการบันทึกมัน - ฉันแค่คิดว่าฉันควร ... มันทำสิ่งแปลก ๆ แม้ว่าฉันจะไม่เข้าใจจริง ๆ - เช่นละเว้น 0x00 นำบางส่วนและยังรวมถึงลำดับของ 1 ไบต์ในสตริง 156 ไบต์ฉัน กำลังเล่นกับ
rafraf

2
นั่นเป็นจุดที่ดีเกี่ยวกับการเป็นผู้นำของ 0 ฉันไม่แน่ใจว่าฉันเชื่อว่ามันสามารถรวมกันเป็นลำดับไบต์และจะสนใจมากที่จะเห็นมันแสดงให้เห็น
Dave L.

1
ใช่ทันทีที่ฉันบอกว่าฉันไม่เชื่อฉันเหมือนกัน :) ฉันวิ่งเปรียบเทียบอาร์เรย์ไบต์จาก BigInteger กับ mmyers'fromHexString และ (โดยไม่ต้อง 0x00) กับสตริงที่ละเมิด - พวกเขาเหมือนกัน "มิกซ์" เกิดขึ้น แต่อาจเป็นอย่างอื่น ฉันจะดูอย่างใกล้ชิดในวันพรุ่งนี้
rafraf

3
ปัญหากับ BigInteger คือต้องมี "sign bit" หากไบต์นำหน้ามีการตั้งค่าบิตสูงแล้วอาร์เรย์ไบต์ผลลัพธ์จะมี 0 พิเศษในตำแหน่งที่ 1 แต่ก็ยัง +1
สีเทา

25

หนึ่งสมุทร:

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

สำหรับผู้ที่คุณสนใจในรหัสจริงที่อยู่เบื้องหลังOne-linersจาก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();
}

3
DatatypeConverter ยังไม่พร้อมใช้งานใน Java 9 โดยค่าเริ่มต้น สิ่งที่อันตรายคือรหัสที่ใช้มันจะคอมไพล์ภายใต้ Java 1.8 หรือเก่ากว่า (Java 9 ที่มีการตั้งค่าซอร์สไปยังก่อนหน้า) แต่รับข้อยกเว้นรันไทม์ภายใต้ Java 9 โดยไม่มี "--add-modules java.se.ee"
Stephen M -on strike-

24

HexBinaryAdapterให้ความสามารถในการผนึกกำลังและ unmarshal ระหว่างและStringbyte[]

import javax.xml.bind.annotation.adapters.HexBinaryAdapter;

public byte[] hexToBytes(String hexString) {
     HexBinaryAdapter adapter = new HexBinaryAdapter();
     byte[] bytes = adapter.unmarshal(hexString);
     return bytes;
}

นั่นเป็นเพียงตัวอย่างที่ฉันพิมพ์ใน ... ฉันแค่ใช้มันอย่างที่เป็นจริงและไม่จำเป็นต้องแยกวิธีการใช้


5
มันจะทำงานเฉพาะในกรณีที่สตริงการป้อนข้อมูล (hexString) มีจำนวนตัวอักษร มิฉะนั้น: ข้อยกเว้นในเธรด "main" java.lang.IllegalArgumentException: hexBinary ต้องมีความยาวเท่ากัน:
ovdsrn

3
โอ้ขอบคุณสำหรับการชี้ให้เห็นว่า ผู้ใช้ไม่ควรมีจำนวนอักขระที่แปลกจริง ๆ เนื่องจากอาร์เรย์ไบต์แสดงเป็น {0x00,0xA0,0xBf} แต่ละไบต์มีเลขฐานสิบหกสองหลักหรือ nibbles ดังนั้นจำนวนไบต์ใด ๆ ควรมีจำนวนตัวอักษรเสมอ ขอบคุณที่พูดถึงสิ่งนี้
GrkEngineer

8
คุณสามารถใช้ java.xml.bind.DatatypeConverter.parseHexBinary (hexString) โดยตรงแทนการใช้ HexBinaryAdapter (ซึ่งจะเรียก DatatypeConverter) วิธีนี้คุณไม่จำเป็นต้องสร้างวัตถุอินสแตนซ์ของอะแดปเตอร์ (เนื่องจากวิธีการ DatatypeConverter เป็นแบบคงที่)
เทรเวอร์ฟรีแมน

javax.xml.bind. * ไม่สามารถใช้ได้ใน Java 9 อีกต่อไปสิ่งที่อันตรายคือรหัสที่ใช้มันจะคอมไพล์ภายใต้ Java 1.8 หรือเก่ากว่า (Java 9 ที่มีการตั้งค่าซอร์สไปยังก่อนหน้า) แต่รับข้อยกเว้นรันไทม์ที่ทำงานภายใต้ Java 9
Stephen M -on strike-

15

นี่คือวิธีการที่ใช้งานได้จริง

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] แต่คุณคงรู้แล้ว


1
ขอบคุณ Michael - คุณเป็นผู้ช่วยชีวิต! ทำงานกับโปรเจ็กต์ BlackBerry และพยายามแปลงการแทนค่าสตริงของไบต์กลับไปเป็นไบต์ ... โดยใช้เมธอด "Byte.parseByte (byteString, 16)" ของ RIM เก็บโยน NumberFormatExcpetion ใช้เวลาหลายชั่วโมงในการหาเหตุผลว่าทำไม ข้อเสนอแนะของคุณของ "Integer.praseInt ()" ได้หลอกลวง ขอบคุณอีกครั้ง!!
BonanzaDriver

12

ในหุ่นยนต์ถ้าคุณกำลังทำงานกับฐานสิบหกคุณสามารถลองOkio

การใช้งานง่าย:

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

และผลลัพธ์จะเป็น

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

ฉันได้ทดสอบวิธีการต่าง ๆ มากมาย แต่วิธีนี้เร็วอย่างน้อยสองเท่า!
poby

5

BigInteger()วิธีจาก java.math เป็นอย่างช้าและไม่ recommandable

Integer.parseInt(HEXString, 16)

อาจทำให้เกิดปัญหากับตัวละครบางตัวโดยไม่แปลงเป็น Digit / Integer

วิธีการทำงานที่ดี:

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

ขอให้โชคดี


4

แก้ไข: ตามที่ระบุโดย @mmyers วิธีนี้จะไม่ทำงานกับอินพุตที่มีสตริงย่อยที่สอดคล้องกับไบต์ด้วยชุดบิตสูง ("80" - "FF") คำอธิบายที่รหัสข้อผิดพลาด: 6259307 Byte.parseByte ไม่ทำงานตามที่ลงโฆษณาในเอกสาร

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

1
ปิด แต่วิธีนี้ล้มเหลวในอินพุตที่กำหนด "00A0BBF" ดูbugs.sun.com/bugdatabase/view_bug.do?bug_id=6259307
Michael Myers

1
นอกจากนี้ยังแปลกมันไม่ได้จัดการกับ "9C"
rafraf

1
@mmyers: โอ้โฮ นั่นไม่ดีเลย ขออภัยที่สับสน @ravigad: 9C มีปัญหาเดียวกันเพราะในกรณีนี้บิตสูงถูกตั้งค่า
แบลร์คอนราด

(byte) Short.parseShort (thisByte, 16) แก้ปัญหานั้น
Jamey Hicks

3

สำหรับสิ่งที่คุ้มค่าต่อไปนี้เป็นรุ่นอื่นที่รองรับสตริงความยาวคี่โดยไม่ต้องใช้การเรียงต่อสตริง

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

2

ฉันมักจะใช้วิธีการเช่น

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

วิธีนี้แยกค่าฐานสิบหกที่คั่นด้วยช่องว่าง แต่มันก็ไม่ยากที่จะแยกสตริงตามเกณฑ์อื่น ๆ เช่นการจัดกลุ่มของอักขระสองตัว


การต่อสตริงไม่จำเป็น เพียงใช้ Integer.valueOf (val, 16)
Michael Myers

ฉันลองใช้การแปลง radix แบบนั้นมาก่อนและฉันได้ผลลัพธ์ที่หลากหลาย
pfranza

ขอบคุณ - แปลกที่มันทำงานได้ดีกับสตริงนี้: "9C001C" หรือ "001C21" และล้มเหลวด้วยอันนี้: "9C001C21" ข้อยกเว้นในเธรด "main" java.lang.NumberFormatException: สำหรับสตริงอินพุต: "9C001C21" ที่ java.lang NumberFormatException.forInputString (ไม่ทราบแหล่งที่มา)
rafraf

(นั่นไม่แปลกไปกว่าในByte/ byteกล่อง: บิตที่สูงที่สุดโดยไม่มีการตั้งค่า -)
greybeard

2

ฉันชอบวิธีแก้ปัญหา 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;
}

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

2
นี่เป็นปัญหาที่แตกต่างอย่างแท้จริงและอาจเป็นของเธรดอื่น
Sean Coffey

1

ฉันพบว่า Kernel Panic มีวิธีแก้ปัญหาที่มีประโยชน์ที่สุดสำหรับฉัน แต่พบปัญหาหากสตริงเลขฐานสิบหกเป็นเลขคี่ แก้ไขด้วยวิธีนี้:

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 ... ] คู่ฐานสิบหก, จากนั้นจำนวนคู่ ...


1

ขึ้นอยู่กับวิธีแก้ปัญหาที่ได้รับการโหวตวิธีต่อไปนี้ควรมีประสิทธิภาพมากกว่านี้เล็กน้อย:

  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


1

หากคุณมีความพึงพอใจสำหรับ Java 8 สตรีมเป็นสไตล์การเขียนโปรแกรมของคุณสิ่งนี้สามารถทำได้โดยใช้ JDK primitives

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

, 0, s2.size()พารามิเตอร์ในฟังก์ชั่นสะสม concatenate IOExceptionสามารถละเว้นถ้าคุณไม่คิดจับ


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

0

ทางออกที่เป็นทางการของฉัน:

/**
 * 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 () Functionแต่ในสไตล์ Java

ตัวอย่าง:

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

0

ไปงานปาร์ตี้สาย แต่ฉันได้รวมคำตอบข้างต้นโดย 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);

        }

    }

}

0

ฉันรู้ว่านี่เป็นด้ายเก่ามาก แต่ก็ยังต้องการเพิ่มเงินของฉัน

ถ้าฉันต้องการรหัสสตริง hex แบบง่าย ๆ ไปยังตัวแปลงไบนารีฉันต้องการทำดังต่อไปนี้

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

0

โดยไกลไม่ใช่วิธีที่สะอาด แต่มันใช้งานได้สำหรับฉันและมีรูปแบบที่ดี:

private String createHexDump(byte[] msg, String description) {
    System.out.println();
    String result = "\n" + description;
    int currentIndex = 0;
    int printedIndex = 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;
                printedIndex++;
            }

            result += String.format("\n  %04x ", i);
        }

        result += String.format("%02x ", msg[i]);
    }

    if(currentIndex - printedIndex > 0){
        int leftOvers = currentIndex - printedIndex;
        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;
}

ผลลัพธ์:

  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                                               | ?

-2

ฉันคิดว่าจะทำเพื่อคุณ ฉัน cobbled มันร่วมกันจากฟังก์ชั่นที่คล้ายกันที่กลับข้อมูลเป็นสตริง:

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

ก่อนอื่นคุณไม่จำเป็นต้องแปลงสตริงเป็นตัวพิมพ์ใหญ่ ประการที่สองมันเป็นไปได้ที่จะผนวกตัวอักษรโดยตรงกับ StringBuffer ซึ่งควรมีประสิทธิภาพมากขึ้น
Michael Myers

-2

สำหรับฉันนี่คือทางออก 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;
}

คำถามนี้ได้รับคำตอบมาระยะหนึ่งแล้วและมีทางเลือกที่ดีหลายทาง น่าเสียดายที่คำตอบของคุณไม่ได้ให้คุณค่าที่ดีขึ้นอย่างมีนัยสำคัญ ณ จุดนี้
rfornal
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.