รหัส Java เพื่อแปลงไบต์เป็นเลขฐานสิบหก


184

ฉันมีอาร์เรย์เป็นไบต์ ฉันต้องการให้แต่ละสตริงของอาร์เรย์นั้นถูกแปลงเป็นค่าเลขฐานสิบหกที่สอดคล้องกัน

มีฟังก์ชั่นใด ๆ ใน Java เพื่อแปลงอาร์เรย์ไบต์เป็นเลขฐานสิบหก?


2
สิ่งที่คุณเรียกใช้ byte-array ใน Java เรียกว่า byte string ในภาษาอื่น (เช่นdocs.racket-lang.org/guide/bytestrings.html )
Patrick Favre

คำตอบ:


311
    byte[] bytes = {-1, 0, 1, 2, 3 };
    StringBuilder sb = new StringBuilder();
    for (byte b : bytes) {
        sb.append(String.format("%02X ", b));
    }
    System.out.println(sb.toString());
    // prints "FF 00 01 02 03 "

ดูสิ่งนี้ด้วย

  • java.util.Formatter วากยสัมพันธ์
    • %[flags][width]conversion
      • ตั้งค่าสถานะ'0'- ผลลัพธ์จะเป็นศูนย์
      • ความกว้าง 2
      • การแปลง'X'- ผลลัพธ์ถูกจัดรูปแบบเป็นเลขจำนวนเต็มฐานสิบหกตัวพิมพ์ใหญ่

ดูที่ข้อความของคำถามอาจเป็นไปได้ว่านี่คือสิ่งที่ขอ:

    String[] arr = {"-1", "0", "10", "20" };
    for (int i = 0; i < arr.length; i++) {
        arr[i] = String.format("%02x", Byte.parseByte(arr[i]));
    }
    System.out.println(java.util.Arrays.toString(arr));
    // prints "[ff, 00, 0a, 14]"

หลายคำตอบที่นี่ใช้Integer.toHexString(int); นี้เป็นไปได้ แต่มีข้อควรระวังบางอย่าง เนื่องจากพารามิเตอร์เป็นintแบบดั้งเดิมการแปลงแบบขยายจะดำเนินการกับbyteอาร์กิวเมนต์ซึ่งเกี่ยวข้องกับส่วนขยายสัญญาณ

    byte b = -1;
    System.out.println(Integer.toHexString(b));
    // prints "ffffffff"

8 บิตbyteซึ่งมีการลงนามใน Java เป็นสัญญาณ-ขยายไปถึง int32 เพื่อประสิทธิภาพในการยกเลิกการขยายเครื่องหมายนี้หนึ่งสามารถหน้ากากด้วยbyte0xFF

    byte b = -1;
    System.out.println(Integer.toHexString(b & 0xFF));
    // prints "ff"

ปัญหาอีกประการของการใช้toHexStringคือไม่ใช้แผ่นกับเลขศูนย์:

    byte b = 10;
    System.out.println(Integer.toHexString(b & 0xFF));
    // prints "a"

ปัจจัยทั้งสองที่รวมกันควรทำให้String.formatโซลูชันเป็นที่นิยมมากกว่า

อ้างอิง


@Vivek: "ค่าขนาดใหญ่มหาศาล" คืออะไร? อินพุตคืออะไรและเอาต์พุตคืออะไร
polygenelubricants

ให้ฉันอธิบายอีกครั้ง .. ฉันมีคอลเลกชันของสตริงไบต์ในอาร์เรย์ แต่สิ่งที่ฉันต้องทำก็คือวิเคราะห์แต่ละไบต์แยกจากกัน .. ดังนั้นฉันไม่ต้องการทำงานในอาร์เรย์ทั้งหมด แต่สตริงไบต์แต่ละรายการในแต่ละครั้งซึ่งเป็นองค์ประกอบหนึ่งของอาร์เรย์นั้น .. ความสับสนเกิดขึ้นเนื่องจากคำว่า " อาร์เรย์" ตอนนี้ในรหัสด้านล่าง "byte bv = 10; สตริง hexString = Integer.toHexString (bv);" CAse 1 (รับไบต์: 68 เอาท์พุท Hex: 44) กรณี: 2 (ไบต์รับ: -46 Hex เอาท์พุท:: ffffffd2) ......... เหตุใดฉันจึงได้รับผลลัพธ์ที่ไม่คาดคิดเช่นนั้นสำหรับบางค่า
Vivek

1
@ Vivek: toHexStringอ่านคำตอบของฉันเกี่ยวกับการใช้ คุณต้องปกปิดมันด้วย& 0xFFเช่นเป็นInteger.toHexString(-46 & 0xFF) "d2"
polygenelubricants

@polygenel น้ำมันหล่อลื่น: ขอบคุณมาก .. ดูเหมือนว่าในที่สุดรหัสก็ใช้ได้ดี .. มันปลอดภัยไหมที่จะใช้ฟังก์ชั่น HexString ตอนนี้เหรอ? หรืออาจมีช่องโหว่ด้วยวิธีการ?
Vivek

1
@Vivek: มัน "ปลอดภัย" คุณแค่ต้องระวังและให้แน่ใจว่าคุณปกปิดbyteค่าด้วย& 0xFFทุกครั้ง formatวิธีการแก้ปัญหาดังกล่าวยังอาจต้องใช้กาวขึ้นอยู่กับสิ่งที่คุณกำลังใช้จริงเป็นอาร์กิวเมนต์
polygenelubricants

65

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

ก่อนอื่นเราจะพยายามทำอะไร เราต้องการแปลงค่าไบต์ (หรืออาร์เรย์ของไบต์) เป็นสตริงซึ่งแทนค่าเลขฐานสิบหกใน ASCII ดังนั้นขั้นตอนที่หนึ่งคือการค้นหาว่าไบต์ใน Java คืออะไร:

ชนิดข้อมูลไบต์เป็นจำนวนเต็มส่วนเติมสองของ 8 บิตที่เซ็นชื่อแล้ว มันมีค่าต่ำสุด -128 และค่าสูงสุด 127 (รวม) ชนิดข้อมูลไบต์มีประโยชน์สำหรับการบันทึกหน่วยความจำในอาร์เรย์ขนาดใหญ่ซึ่งการประหยัดหน่วยความจำมีความสำคัญ พวกเขายังสามารถใช้แทน int ที่ขีด จำกัด ของพวกเขาช่วยในการชี้แจงรหัสของคุณ ความจริงที่ว่าช่วงของตัวแปรนั้นมี จำกัด สามารถใช้เป็นรูปแบบของเอกสารได้

สิ่งนี้หมายความว่า? บางสิ่งที่แรกและที่สำคัญที่สุดก็หมายความว่าเรากำลังทำงานกับ8 บิต ตัวอย่างเช่นเราสามารถเขียนหมายเลข 2 เป็น 0000 0010 อย่างไรก็ตามเนื่องจากมันเป็นส่วนเติมเต็มของสองเราจึงเขียน 2 ลบเช่นนี้: 1111 1110 นอกจากนี้ยังหมายความว่าการแปลงเป็นเลขฐานสิบหกนั้นตรงไปตรงมามาก นั่นคือคุณเพียงแปลงเซ็กเมนต์ 4 บิตโดยตรงเป็นฐานสิบหก โปรดทราบว่าเพื่อให้เข้าใจถึงจำนวนลบในโครงร่างนี้ก่อนอื่นคุณจะต้องเข้าใจส่วนประกอบสองอย่างก่อน หากคุณยังไม่เข้าใจส่วนประกอบสองอย่างคุณสามารถอ่านคำอธิบายที่ดีเยี่ยมได้ที่นี่: http://www.cs.cornell.edu/~tomf/notes/cps104/twoscomp.html


การแปลงความสมบูรณ์ของ Two เป็น Hex โดยทั่วไป

เมื่อตัวเลขอยู่ในส่วนเติมเต็มของสองมันเป็นการง่ายที่จะแปลงให้เป็นเลขฐานสิบหก โดยทั่วไปแล้วการแปลงจากเลขฐานสองเป็นฐานสิบหกนั้นตรงไปตรงมามากและอย่างที่คุณเห็นในสองตัวอย่างถัดไป

ตัวอย่าง

ตัวอย่างที่ 1:แปลง 2 เป็น Hex

1) แปลง 2 เป็นไบนารีเป็นครั้งแรกในส่วนเติมเต็มสอง:

2 (base 10) = 0000 0010 (base 2)

2) ตอนนี้แปลงเลขฐานสองเป็นฐานสิบหก:

0000 = 0x0 in hex
0010 = 0x2 in hex

therefore 2 = 0000 0010 = 0x02. 

ตัวอย่างที่ 2:แปลง -2 (ในส่วนเติมเต็มสองของ) เป็น Hex

1) ก่อนแปลง -2 เป็นไบนารีในส่วนเติมเต็มของสอง:

-2 (base 10) = 0000 0010 (direct conversion to binary) 
               1111 1101 (invert bits)
               1111 1110 (add 1)
therefore: -2 = 1111 1110 (in two's complement)

2) แปลงเป็น Hex:

1111 = 0xF in hex
1110 = 0xE in hex

therefore: -2 = 1111 1110 = 0xFE.


ทำสิ่งนี้ใน Java

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

byte[] bytes = new byte[]{-2,2};

เราเพิ่งแปลงมันให้เป็นเลขฐานสิบหกด้านบนด้วยตนเอง แต่เราจะทำใน Java ได้อย่างไร นี่คือวิธี:

ขั้นตอนที่ 1:สร้าง StringBuffer เพื่อเก็บการคำนวณของเรา

StringBuffer buffer = new StringBuffer();

ขั้นตอนที่ 2:แยกบิตลำดับที่สูงกว่าแปลงให้เป็นเลขฐานสิบหกและผนวกเข้ากับบัฟเฟอร์

ด้วยเลขฐานสอง 1111 1110 เราสามารถแยกบิตคำสั่งที่สูงขึ้นได้โดยการเลื่อนพวกมันไป 4 ครั้งแรกจากนั้นก็ลดจำนวนที่เหลือลง เหตุผลนี้ง่าย แต่รายละเอียดการใช้งานใน Java (และหลายภาษา) แนะนำรอยย่นเนื่องจากการขยายสัญญาณ โดยพื้นฐานแล้วเมื่อคุณเลื่อนค่าไบต์ Java จะแปลงค่าของคุณเป็นจำนวนเต็มก่อนแล้วจึงทำการเพิ่มส่วนขยายสัญญาณ ดังนั้นในขณะที่คุณคาดหวังว่า 1111 1110 >> 4 จะเป็น 0000 1111 ในความเป็นจริงใน Java มันจะแสดงเป็น 0xFFFFFFFF ที่สมบูรณ์ของทั้งสอง!

ดังนั้นกลับไปที่ตัวอย่างของเรา:

1111 1110 >> 4 (shift right 4) = 1111 1111 1111 1111 1111 1111 1111 1111 (32 bit sign-extended number in two's complement)

จากนั้นเราสามารถแยกบิตด้วยหน้ากาก:

1111 1111 1111 1111 1111 1111 1111 1111 & 0xF = 0000 0000 0000 0000 0000 0000 0000 1111
therefore: 1111 = 0xF in hex. 

ในจาวาเราสามารถทำได้ทั้งหมดในนัดเดียว:

Character.forDigit((bytes[0] >> 4) & 0xF, 16);

ฟังก์ชัน forDigit เพียงจับคู่หมายเลขที่คุณส่งผ่านไปยังชุดเลขฐานสิบหก 0-F

ขั้นตอนที่ 3:ถัดไปเราต้องแยกบิตลำดับล่าง เนื่องจากบิตที่เราต้องการอยู่ในตำแหน่งที่ถูกต้องเราจึงสามารถซ่อนมันได้:

1111 1110 & 0xF = 0000 0000 0000 0000 0000 0000 0000 1110 (recall sign extension from before)
therefore: 1110 = 0xE in hex.  

อย่างที่เคยเป็นมาใน Java เราสามารถทำได้ทั้งหมดในนัดเดียว:

Character.forDigit((bytes[0] & 0xF), 16);

การรวมทั้งหมดเข้าด้วยกันเราสามารถทำมันเพื่อ for loop และแปลง array ทั้งหมด:

for(int i=0; i < bytes.length; i++){
    buffer.append(Character.forDigit((bytes[i] >> 4) & 0xF, 16));
    buffer.append(Character.forDigit((bytes[i] & 0xF), 16));
}

หวังว่าคำอธิบายนี้จะทำให้สิ่งต่าง ๆ ชัดเจนขึ้นสำหรับคนที่คุณสงสัยว่าเกิดอะไรขึ้นในตัวอย่างมากมายที่คุณจะพบบนอินเทอร์เน็ต หวังว่าฉันจะไม่ได้ทำผิดพลาดร้ายแรง แต่คำแนะนำและการแก้ไขยินดีต้อนรับอย่างมาก!


4
คำตอบที่ดีที่สุด! การดำเนินการทางสมมาตรของสตริงสตริงฐานสิบหกเป็นไบต์จะเปลี่ยนจากนั้นใช้Character.digit()เช่น(byte) ((Character.digit(str.charAt(0), 16) << 4) + Character.digit(str.charAt(1), 16))
ericbn

21

เร็วที่สุดวิธีที่ฉันได้พบยังไม่ได้ทำเช่นนี้คือต่อไปนี้:

private static final String    HEXES    = "0123456789ABCDEF";

static String getHex(byte[] raw) {
    final StringBuilder hex = new StringBuilder(2 * raw.length);
    for (final byte b : raw) {
        hex.append(HEXES.charAt((b & 0xF0) >> 4)).append(HEXES.charAt((b & 0x0F)));
    }
    return hex.toString();
}

มัน ~ 50x String.formatเร็วกว่า หากคุณต้องการทดสอบ:

public class MyTest{
    private static final String    HEXES        = "0123456789ABCDEF";

    @Test
    public void test_get_hex() {
        byte[] raw = {
            (byte) 0xd0, (byte) 0x0b, (byte) 0x01, (byte) 0x2a, (byte) 0x63,
            (byte) 0x78, (byte) 0x01, (byte) 0x2e, (byte) 0xe3, (byte) 0x6c,
            (byte) 0xd2, (byte) 0xb0, (byte) 0x78, (byte) 0x51, (byte) 0x73,
            (byte) 0x34, (byte) 0xaf, (byte) 0xbb, (byte) 0xa0, (byte) 0x9f,
            (byte) 0xc3, (byte) 0xa9, (byte) 0x00, (byte) 0x1e, (byte) 0xd5,
            (byte) 0x4b, (byte) 0x89, (byte) 0xa3, (byte) 0x45, (byte) 0x35,
            (byte) 0xd6, (byte) 0x10,
        };

        int N = 77777;
        long t;

        {
            t = System.currentTimeMillis();
            for (int i = 0; i < N; i++) {
                final StringBuilder hex = new StringBuilder(2 * raw.length);
                for (final byte b : raw) {
                    hex.append(HEXES.charAt((b & 0xF0) >> 4)).append(HEXES.charAt((b & 0x0F)));
                }
                hex.toString();
            }
            System.out.println(System.currentTimeMillis() - t); // 50
        }

        {
            t = System.currentTimeMillis();
            for (int i = 0; i < N; i++) {
                StringBuilder hex = new StringBuilder(2 * raw.length);
                for (byte b : raw) {
                    hex.append(String.format("%02X", b));
                }
                hex.toString();
            }
            System.out.println(System.currentTimeMillis() - t); // 2535
        }

    }
}

แก้ไข : เพิ่งพบบางสิ่งเพียง lil เร็วขึ้นและที่หนึ่งบรรทัด แต่เข้ากันไม่ได้กับ JRE 9 ใช้ความเสี่ยงของคุณเอง

import javax.xml.bind.DatatypeConverter;

DatatypeConverter.printHexBinary(raw);

2
DatatypeConverter ไม่สามารถใช้งานได้ใน Java 9 สิ่งที่อันตรายคือรหัสที่ใช้จะรวบรวมภายใต้ Java 1.8 หรือรุ่นก่อนหน้า (Java 9 ที่มีการตั้งค่าซอร์สไปก่อนหน้า) แต่รับข้อยกเว้น runtime ภายใต้ Java 9
Stephen M -on strike-

ฉันจุดที่สอง @StephenMs: การใช้สิ่งนี้กับ jre9 จะมีปัญหากับ ClassNotFound Exception
Patrick Favre

ที่จริงแล้วสามารถแยกวิธีซอร์สโค้ด printHexBinary จาก src.zip ของ jdk ซึ่งดูเหมือนว่าเร็วกว่าวิธีที่ 1 ถึง 1 เท่า
ผลไม้

1
หากคุณทำงานกับ char char สำหรับค่าคงที่ HEXES แทน String และ charAt () คุณจะได้รับความเร็วเพิ่มขึ้น ~ 20%
Dyorgio

15

ลองด้วยวิธีนี้:

byte bv = 10;
String hexString = Integer.toHexString(bv);

การจัดการกับอาร์เรย์ (ถ้าฉันเข้าใจคุณถูกต้อง):

byte[] bytes = {9, 10, 11, 15, 16};
StringBuffer result = new StringBuffer();
for (byte b : bytes) {
    result.append(String.format("%02X ", b));
    result.append(" "); // delimiter
}
return result.toString();

ในฐานะที่เป็น polygenelubricants กล่าวString.format()เป็นคำตอบที่ถูกเปรียบเทียบกับInteger.toHexString()(ตั้งแต่มันเกี่ยวข้องกับตัวเลขติดลบในทางขวา)


2
-1นี้จะลงนามขยายเช่นลอง
polygenelubricants

ไบต์ bv = 10; String hexString = Integer.toHexString (bv); ดูเหมือนว่ามันจะทำงานได้ .. ฉันสามารถใช้มันทีละรายการในทุก ๆ องค์ประกอบของอาเรย์ .. โค้ดอื่น ๆ (การจัดการกับอาเรย์) ให้ค่ามากเกินไป สิ่งที่อาจเป็นสาเหตุของการทำเช่นนั้น?
Vivek

@ Vivek ว่าเป็นเพราะในกรณีที่bvจะส่งกลับอักขระเลขฐานสิบหกเดียว ในขณะที่ส่วนที่เหลือของรหัสส่งกลับสายอักขระเลขฐานสิบหก ฉันเปลี่ยนรหัสด้วย delimeter เพื่อให้คุณเข้าใจได้ทันที
0x2D9A3

@Bar: คุณยังคงสามารถใช้งานได้Integer.toHexStringหากคุณปิดบังส่วนขยายbytewith 0xFFเพื่อเลิกทำการลงชื่อเข้าใช้
polygenelubricants

CAse 1 (ได้รับ Byte: 68 Hex Output:: 44) Case: 2 (Byte Recieveed: -46 Hex Output:: ffffffd2) ฉันได้รับค่าเอาท์พุทที่ไม่คาดคิดในกรณีของอาร์เรย์ไบต์เชิงลบ ...
Vivek

13

ทางออกที่ดีที่สุดคือ badass หนึ่งซับ:

String hex=DatatypeConverter.printHexBinary(byte[] b);

ดังกล่าวที่นี่


4
DatatypeConverter ไม่สามารถใช้งานได้ใน Java 9 สิ่งที่อันตรายคือรหัสที่ใช้จะรวบรวมภายใต้ Java 1.8 หรือรุ่นก่อนหน้า (Java 9 ที่มีการตั้งค่าซอร์สไปยังก่อนหน้า) แต่รับยกเว้น runtime ภายใต้ Java 9
Stephen M -on strike-

เศร้าเมื่อคุณพูดมันไม่ได้อยู่ใน jdk9 new BigInteger(byteArray).toString(16)เป็นวิธีที่จะไปแล้ว เป็นปัญหาที่สมบูรณ์แบบด้วยหรือไม่?
prayagupd

อาจไม่ใช่ปัญหาที่สมบูรณ์ แต่มันจะพลาดศูนย์นำหน้า (เนื่องจากมันไม่มีความหมายกับตัวเลขอย่าง BigInteger is)
Friso

ดูเหมือนว่าจะยังอยู่ในจาวา 9 เอกสารดังนั้นดูเหมือนว่ามันโอเคที่จะใช้มันจากสิ่งที่ฉันสามารถบอกได้
Brad Parks

ฉันคิดว่าตามที่อธิบายไว้ที่นี่มันก็ยังโอเคที่จะใช้สำหรับ java9 แต่จะถูกลบออกในรุ่น Java ในอนาคต คุณยังจะยังคงสามารถที่จะใช้มันกับ 'ใหม่' โมดูลแบบสแตนด์อโลน JAXB ณเวอร์ชัน 2.3.0
คม

11

หากคุณต้องการใช้การแทนค่าฐานสิบหกแบบความกว้างคงที่เช่น0AแทนAเพื่อให้คุณสามารถกู้คืนไบต์ได้อย่างไม่น่าสงสัยลองformat():

StringBuilder result = new StringBuilder();
for (byte bb : byteArray) {
    result.append(String.format("%02X", bb));
}
return result.toString();

11

วิธีที่สั้นและง่ายในการแปลงbyte[]เป็นสตริงฐานสิบหกโดยใช้BigInteger:

import java.math.BigInteger;

byte[] bytes = new byte[] {(byte)255, 10, 20, 30};
String hex = new BigInteger(1, bytes).toString(16);
System.out.println(hex); // ff0a141e

มันทำงานอย่างไร?

คลาสคลาสระบบในตัวjava.math.BigInteger( java.math.BigInteger ) เข้ากันได้กับข้อมูลไบนารีและฐานสิบหก:

  • มันมีนวกรรมิกBigInteger(signum=1, byte[])ในการสร้างจำนวนเต็มขนาดใหญ่โดยbyte[](ตั้งค่าพารามิเตอร์แรกsignum= 1เพื่อจัดการอย่างถูกต้องไบต์ลบ)
  • ใช้BigInteger.toString(16)เพื่อแปลงจำนวนเต็มใหญ่เป็นสตริงฐานสิบหก
  • ในการแยกวิเคราะห์ตัวเลขฐานสิบหกใช้new BigInteger("ffa74b", 16)- ไม่จัดการอย่างถูกต้องศูนย์นำหน้า

หากคุณต้องการให้มีศูนย์นำหน้าในผลลัพธ์เลขฐานสิบหกให้ตรวจสอบขนาดและเพิ่มศูนย์ที่ขาดหายไปหากจำเป็น:

if (hex.length() % 2 == 1)
    hex = "0" + hex;

หมายเหตุ

ใช้new BigInteger(1, bytes)แทนnew BigInteger(bytes)เพราะ Java ถูก "ใช้งานไม่ได้ " และbyteชนิดข้อมูลไม่ได้เก็บเป็นไบต์ แต่เป็นจำนวนเต็มเล็ก ๆ ที่เซ็นชื่อ [-128 ... 127] ถ้าไบต์แรกเป็นลบBigIntegerจะถือว่าคุณผ่านจำนวนเต็มลบที่ใหญ่ เพียงผ่าน1เป็นพารามิเตอร์แรก ( signum=1)

การแปลงกลับจากฐานสิบหกไปbyte[]เป็นเรื่องยุ่งยาก: บางครั้งศูนย์นำหน้าจะเข้าสู่ผลลัพธ์ที่ผลิตและควรทำความสะอาดดังนี้:

byte[] bytes = new BigInteger("ffa74b", 16).toByteArray();
if (bytes[0] == 0) {
    byte[] newBytes = new byte[bytes.length - 1];
    System.arraycopy(bytes, 1, newBytes, 0, newBytes.length);
    bytes = newBytes;
}

หมายเหตุสุดท้ายคือถ้าbyte[]มีเลขศูนย์นำหลายค่าจะหายไป


1
หากไบต์นำหน้ามีค่าทศนิยมน้อยกว่า 16 คุณจะได้รับสตริงที่มีตัวเลขฐานสิบหก
Alex Jorgenson

8

หากคุณมีความสุขในการใช้ห้องสมุดภายนอกorg.apache.commons.codec.binary.Hexชั้นมีencodeHexวิธีการที่จะใช้เวลาและผลตอบแทนbyte[] char[]วิธีนี้เร็วกว่าตัวเลือกการจัดรูปแบบมากและสรุปรายละเอียดของการแปลง ยังมาพร้อมกับdecodeHexวิธีการแปลงตรงกันข้าม


4
วิธีที่ง่ายยิ่งขึ้นคือการใช้ฟังก์ชันในตัว javax.xml.bind.DatatypeConverter / parseHexBinary และ printHexBinary ดูstackoverflow.com/questions/9655181/…
Alan Thompson

2
+1 ตัวเลือกนี้ Hex ยังมีเมธอด encodeHexString ซึ่งรับค่าเป็นไบต์ [] และส่งคืนสตริง
Mingjiang Shi

อย่าลืมว่าjavaxเนมสเปซไม่สามารถใช้งานได้เสมอ
Mene

7

คุณสามารถใช้วิธีการจากห้องสมุดBouncy Castle Provider :

org.bouncycastle.util.encoders.Hex.toHexString(byteArray);

แพ็คเกจ Bouncy Castle Crypto เป็นการใช้งานจาวาของอัลกอริธึมการเข้ารหัส jar นี้มีผู้ให้บริการ JCE และ API น้ำหนักเบาสำหรับ Bouncy Castle Cryptography APIs สำหรับ JDK 1.5 ถึง JDK 1.8

การพึ่งพา Maven:

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.60</version>
</dependency>

หรือจากApache Commons Codec :

org.apache.commons.codec.binary.Hex.encodeHexString(byteArray);

แพ็คเกจ Apache Commons Codec มีตัวเข้ารหัสและตัวถอดรหัสอย่างง่ายสำหรับรูปแบบต่างๆเช่น Base64 และเลขฐานสิบหก นอกจากตัวเข้ารหัสและตัวถอดรหัสที่ใช้กันอย่างแพร่หลายแล้วแพ็คเกจตัวแปลงสัญญาณนี้ยังเก็บรวบรวมยูทิลิตี้การเข้ารหัสแบบออกเสียง

การพึ่งพา Maven:

<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.11</version>
</dependency>

ใช่นี่เป็นทางออกที่ดีที่สุดแต่ต้องใช้ไลบรารีภายนอก: Apache Commons Codec ( mvnrepository.com/artifact/commons-codec/commons-codec/1.11 ) หรือผู้ให้บริการ BouncyCastle ( mvnrepository.com/artifact/org.bouncycastle/bcprov- jdk15on / 1.60 )
Svetlin Nakov

5

นี่คือรหัสที่ฉันพบว่าทำงานได้เร็วที่สุด ฉันวิ่งไปที่อาร์เรย์ 1,09015 ไบต์ที่มีความยาว 32 ใน 23 มิลลิวินาที ฉันทำงานบน VM ดังนั้นมันอาจจะทำงานได้เร็วขึ้นบนโลหะเปลือย

public static final char[] HEX_DIGITS =         {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};

public static char[] encodeHex( final byte[] data ){
    final int l = data.length;
    final char[] out = new char[l<<1];
    for( int i=0,j=0; i<l; i++ ){
        out[j++] = HEX_DIGITS[(0xF0 & data[i]) >>> 4];
        out[j++] = HEX_DIGITS[0x0F & data[i]];
    }
    return out;
}

จากนั้นคุณสามารถทำได้

String s = new String( encodeHex(myByteArray) );

3
BigInteger n = new BigInteger(byteArray);
String hexa = n.toString(16));

ใช้งานไม่ได้: BigInteger(byteArrayOf(-1, 2, 3, 4, 5)).toString(16)ส่งคืน"-fdfcfbfb"
Martin Vysny

มันเป็นผลลัพธ์ที่ถูกต้อง คุณทำงานกับไบต์ '-1', '2' ... '5' ไบต์นั้นไม่มี visualiztion ( unicode.org ) หากคุณต้องการทำงานกับ '-1', '2' ... '5' ตามตัวอักษรคุณควรทำงานกับค่าสตริง
Wender

มันเป็นผลลัพธ์ที่ผิด ค่าไบต์ Java -1 ในความเป็นจริง 0xFF (มันเหมือนกับ(int) 255) ตั้งแต่ไบต์ Java FF02030405เป็นลงนามเพื่อให้ผลที่ควรจะเป็น หากคุณลองใช้ @Jerinaw solution ด้านบนคุณจะเห็นว่ามันจะพิมพ์เอาต์พุตที่ถูกต้อง โปรดดูวิธีแก้ปัญหาของ Svetlin Nakov ด้านล่าง
Martin Vysny

2

นี่เป็นฟังก์ชั่นง่าย ๆ ในการแปลงไบต์เป็นเลขฐานสิบหก

   private static String convertToHex(byte[] data) {
    StringBuffer buf = new StringBuffer();
    for (int i = 0; i < data.length; i++) {
        int halfbyte = (data[i] >>> 4) & 0x0F;
        int two_halfs = 0;
        do {
            if ((0 <= halfbyte) && (halfbyte <= 9))
                buf.append((char) ('0' + halfbyte));
            else
                buf.append((char) ('a' + (halfbyte - 10)));
            halfbyte = data[i] & 0x0F;
        } while(two_halfs++ < 1);
    }
    return buf.toString();
}

2

คนอื่น ๆ ได้ครอบคลุมกรณีทั่วไป แต่ถ้าคุณมีอาร์เรย์ของรูปแบบที่รู้จักตัวอย่างเช่นที่อยู่ MAC คุณสามารถ:

byte[] mac = { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00 };

String str = String.format("%02X:%02X:%02X:%02X:%02X:%02X",
                           mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); 

1

การสร้าง (และทำลาย) กลุ่มStringอินสแตนซ์ไม่ใช่วิธีที่ดีถ้าประสิทธิภาพเป็นปัญหา

โปรดเพิกเฉยคำแถลงการตรวจสอบคำสั่งifเหล่านี้ นั่นคือเพื่อการศึกษา (อีก)

โครงการ maven เต็มรูปแบบ: http://jinahya.googlecode.com/svn/trunk/com.googlecode.jinahya/hex-codec/

การเข้ารหัส ...

/**
 * Encodes a single nibble.
 *
 * @param decoded the nibble to encode.
 *
 * @return the encoded half octet.
 */
protected static int encodeHalf(final int decoded) {

    switch (decoded) {
        case 0x00:
        case 0x01:
        case 0x02:
        case 0x03:
        case 0x04:
        case 0x05:
        case 0x06:
        case 0x07:
        case 0x08:
        case 0x09:
            return decoded + 0x30; // 0x30('0') - 0x39('9')
        case 0x0A:
        case 0x0B:
        case 0x0C:
        case 0x0D:
        case 0x0E:
        case 0x0F:
            return decoded + 0x57; // 0x41('a') - 0x46('f')
        default:
            throw new IllegalArgumentException("illegal half: " + decoded);
    }
}


/**
 * Encodes a single octet into two nibbles.
 *
 * @param decoded the octet to encode.
 * @param encoded the array to which each encoded nibbles are written.
 * @param offset the offset in the array.
 */
protected static void encodeSingle(final int decoded, final byte[] encoded,
                                   final int offset) {

    if (encoded == null) {
        throw new IllegalArgumentException("null encoded");
    }

    if (encoded.length < 2) {
        // not required
        throw new IllegalArgumentException(
            "encoded.length(" + encoded.length + ") < 2");
    }

    if (offset < 0) {
        throw new IllegalArgumentException("offset(" + offset + ") < 0");
    }

    if (offset >= encoded.length - 1) {
        throw new IllegalArgumentException(
            "offset(" + offset + ") >= encoded.length(" + encoded.length
            + ") - 1");
    }

    encoded[offset] = (byte) encodeHalf((decoded >> 4) & 0x0F);
    encoded[offset + 1] = (byte) encodeHalf(decoded & 0x0F);
}


/**
 * Decodes given sequence of octets into a sequence of nibbles.
 *
 * @param decoded the octets to encode
 *
 * @return the encoded nibbles.
 */
protected static byte[] encodeMultiple(final byte[] decoded) {

    if (decoded == null) {
        throw new IllegalArgumentException("null decoded");
    }

    final byte[] encoded = new byte[decoded.length << 1];

    int offset = 0;
    for (int i = 0; i < decoded.length; i++) {
        encodeSingle(decoded[i], encoded, offset);
        offset += 2;
    }

    return encoded;
}


/**
 * Encodes given sequence of octets into a sequence of nibbles.
 *
 * @param decoded the octets to encode.
 *
 * @return the encoded nibbles.
 */
public byte[] encode(final byte[] decoded) {

    return encodeMultiple(decoded);
}

ถอดรหัส ...

/**
 * Decodes a single nibble.
 *
 * @param encoded the nibble to decode.
 *
 * @return the decoded half octet.
 */
protected static int decodeHalf(final int encoded) {

    switch (encoded) {
        case 0x30: // '0'
        case 0x31: // '1'
        case 0x32: // '2'
        case 0x33: // '3'
        case 0x34: // '4'
        case 0x35: // '5'
        case 0x36: // '6'
        case 0x37: // '7'
        case 0x38: // '8'
        case 0x39: // '9'
            return encoded - 0x30;
        case 0x41: // 'A'
        case 0x42: // 'B'
        case 0x43: // 'C'
        case 0x44: // 'D'
        case 0x45: // 'E'
        case 0x46: // 'F'
            return encoded - 0x37;
        case 0x61: // 'a'
        case 0x62: // 'b'
        case 0x63: // 'c'
        case 0x64: // 'd'
        case 0x65: // 'e'
        case 0x66: // 'f'
            return encoded - 0x57;
        default:
            throw new IllegalArgumentException("illegal half: " + encoded);
    }
}


/**
 * Decodes two nibbles into a single octet.
 *
 * @param encoded the nibble array.
 * @param offset the offset in the array.
 *
 * @return decoded octet.
 */
protected static int decodeSingle(final byte[] encoded, final int offset) {

    if (encoded == null) {
        throw new IllegalArgumentException("null encoded");
    }

    if (encoded.length < 2) {
        // not required
        throw new IllegalArgumentException(
            "encoded.length(" + encoded.length + ") < 2");
    }

    if (offset < 0) {
        throw new IllegalArgumentException("offset(" + offset + ") < 0");
    }

    if (offset >= encoded.length - 1) {
        throw new IllegalArgumentException(
            "offset(" + offset + ") >= encoded.length(" + encoded.length
            + ") - 1");
    }

    return (decodeHalf(encoded[offset]) << 4)
           | decodeHalf(encoded[offset + 1]);
}


/**
 * Encodes given sequence of nibbles into a sequence of octets.
 *
 * @param encoded the nibbles to decode.
 *
 * @return the encoded octets.
 */
protected static byte[] decodeMultiple(final byte[] encoded) {

    if (encoded == null) {
        throw new IllegalArgumentException("null encoded");
    }

    if ((encoded.length & 0x01) == 0x01) {
        throw new IllegalArgumentException(
            "encoded.length(" + encoded.length + ") is not even");
    }

    final byte[] decoded = new byte[encoded.length >> 1];

    int offset = 0;
    for (int i = 0; i < decoded.length; i++) {
        decoded[i] = (byte) decodeSingle(encoded, offset);
        offset += 2;
    }

    return decoded;
}


/**
 * Decodes given sequence of nibbles into a sequence of octets.
 *
 * @param encoded the nibbles to decode.
 *
 * @return the decoded octets.
 */
public byte[] decode(final byte[] encoded) {

    return decodeMultiple(encoded);
}

1

นี่เป็นวิธีที่รวดเร็วมาก ไม่จำเป็นต้องใช้ไลบรารีภายนอก

final protected static char[] HEXARRAY = "0123456789abcdef".toCharArray();

    public static String encodeHexString( byte[] bytes ) {

        char[] hexChars = new char[bytes.length * 2];
        for (int j = 0; j < bytes.length; j++) {
            int v = bytes[j] & 0xFF;
            hexChars[j * 2] = HEXARRAY[v >>> 4];
            hexChars[j * 2 + 1] = HEXARRAY[v & 0x0F];
        }
        return new String(hexChars);
    }

1

ฉันไม่สามารถเข้าใจได้ว่าคุณหมายถึงอะไร byte String แต่นี่คือการแปลงจาก byte เป็น String และในทางกลับกันแน่นอนว่ามีอีกมากในเอกสารอย่างเป็นทางการ

Integer intValue = 149;

ค่าไบต์ที่สอดคล้องกันคือ:

Byte byteValue = intValue.byteValue(); // this will convert the rightmost byte of the intValue to byte, because Byte is an 8 bit object and Integer is at least 16 bit, and it will give you a signed number in this case -107

รับค่าจำนวนเต็มกลับจากตัวแปร Byte:

Integer anInt = byteValue.intValue(); // This will convert the byteValue variable to a signed Integer

จาก Byte และ Integer ถึง Hex String:
นี่คือวิธีที่ฉันทำ:

Integer anInt = 149
Byte aByte = anInt.byteValue();

String hexFromInt = "".format("0x%x", anInt); // This will output 0x95
String hexFromByte = "".format("0x%x", aByte); // This will output 0x95

การแปลงอาร์เรย์ของไบต์เป็นสตริง
เลขฐานสิบหก:เท่าที่ฉันรู้ว่าไม่มีฟังก์ชั่นที่ง่ายในการแปลงองค์ประกอบทั้งหมดภายในอาร์เรย์ของบางส่วนObjectเป็นองค์ประกอบของอีกองค์ประกอบหนึ่งObjectดังนั้นคุณต้องทำด้วยตัวเอง คุณสามารถใช้ฟังก์ชั่นต่อไปนี้:

จากไบต์ [] ถึงสตริง:

    public static String byteArrayToHexString(byte[] byteArray){
        String hexString = "";

        for(int i = 0; i < byteArray.length; i++){
            String thisByte = "".format("%x", byteArray[i]);
            hexString += thisByte;
        }

        return hexString;
    }

และจากสตริงฐานสิบถึงไบต์ []:

public static byte[] hexStringToByteArray(String hexString){
    byte[] bytes = new byte[hexString.length() / 2];

    for(int i = 0; i < hexString.length(); i += 2){
        String sub = hexString.substring(i, i + 2);
        Integer intVal = Integer.parseInt(sub, 16);
        bytes[i / 2] = intVal.byteValue();
        String hex = "".format("0x%x", bytes[i / 2]);
    }

    return bytes;
}  

มันสายเกินไป แต่ฉันหวังว่าสิ่งนี้จะช่วยคนอื่นได้;)


1

มีวิธีการที่รวดเร็วของคุณ:

    private static final String[] hexes = new String[]{
        "00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F",
        "10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F",
        "20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F",
        "30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F",
        "40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F",
        "50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F",
        "60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F",
        "70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F",
        "80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F",
        "90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F",
        "A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF",
        "B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF",
        "C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF",
        "D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF",
        "E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF",
        "F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF"
    };

    public static String byteToHex(byte b) {
        return hexes[b&0xFF];
    }

1
น่าเกลียด แต่อาจมีประสิทธิภาพมาก :-)
Rich S

1

เช่นเดียวกับคำตอบอื่น ๆ บางอย่างผมขอแนะนำให้ใช้และString.format() BigIntegerแต่ในการตีความอาร์เรย์ไบต์เป็นตัวแทนไบนารีขนาดใหญ่ endian แทนการเป็นตัวแทนไบนารีสองของประกอบ (กับ signum และการใช้ช่วงค่าฐานสิบหกที่ไม่สมบูรณ์) ใช้BigInteger (int signum, ไบต์ [] ขนาด)ไม่ใช่BigInteger (byte [] val ) .

ตัวอย่างเช่นสำหรับอาร์เรย์ไบต์ที่มีความยาว 8 ให้ใช้:

String.format("%016X", new BigInteger(1,bytes))

ข้อดี:

  • ศูนย์ชั้นนำ
  • ไม่มีสัญลักษณ์
  • ฟังก์ชั่นในตัวเท่านั้น
  • รหัสเดียวเท่านั้น

ข้อด้อย:

  • อาจมีวิธีที่มีประสิทธิภาพมากขึ้นในการทำเช่นนั้น

ตัวอย่าง:

byte[] bytes = new byte[8];
Random r = new Random();
System.out.println("big-endian       | two's-complement");
System.out.println("-----------------|-----------------");
for (int i = 0; i < 10; i++) {
    r.nextBytes(bytes);
    System.out.print(String.format("%016X", new BigInteger(1,bytes)));
    System.out.print(" | ");
    System.out.print(String.format("%016X", new BigInteger(bytes)));
    System.out.println();
}

ตัวอย่างผลลัพธ์:

big-endian       | two's-complement
-----------------|-----------------
3971B56BC7C80590 | 3971B56BC7C80590
64D3C133C86CCBDC | 64D3C133C86CCBDC
B232EFD5BC40FA61 | -4DCD102A43BF059F
CD350CC7DF7C9731 | -32CAF338208368CF
82CDC9ECC1BC8EED | -7D3236133E437113
F438C8C34911A7F5 | -BC7373CB6EE580B
5E99738BE6ACE798 | 5E99738BE6ACE798
A565FE5CE43AA8DD | -5A9A01A31BC55723
032EBA783D2E9A9F | 032EBA783D2E9A9F
8FDAA07263217ABA | -70255F8D9CDE8546

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