วิธีการแปลงอาร์เรย์ไบต์เป็นสตริงฐานสิบหกใน Java?


649

ฉันมีอาร์เรย์ไบต์ที่เต็มไปด้วยเลขฐานสิบหกและการพิมพ์วิธีที่ง่าย ๆ นั้นไร้ประโยชน์เพราะมีองค์ประกอบที่ไม่สามารถพิมพ์ได้จำนวนมาก สิ่งที่ฉันต้องการคือ hexcode ที่ถูกต้องในรูปแบบของ:3a5f771c


12
ทำไมไม่ลองแค่ลองก่อนและแสดงให้เราเห็นว่าคุณมีอะไรบ้าง คุณไม่มีอะไรจะเสียและทั้งหมดจะได้รับ จำนวนเต็มมีtoHexString(...)วิธีการที่อาจช่วยได้หากนี่คือสิ่งที่คุณกำลังมองหา นอกจากนี้ยังString.format(...)สามารถทำเทคนิคการจัดรูปแบบเรียบร้อยโดยใช้%2xสตริงรหัส
เรือที่แล่นเต็มไปด้วยปลาไหล


"สิ่งที่ฉันต้องการคือ hexcode ที่แน่นอนในรูปแบบของ: 3a5f771c ... " - คุณขอแบบฟอร์มที่แน่นอน แต่คุณไม่ได้ให้ตัวอย่างที่แน่นอน ดำเนินการตามที่คุณระบุให้แปลงสี่ไบต์แรกเป็นสตริงจากนั้นเชื่อมต่อจุดไข่ปลากับสตริง
jww

1
ด้วยความช่วยเหลือของกระแสใน Java 8 ก็สามารถนำมาใช้เป็นเพียงแค่: สตริงคงที่ byteArrayToHex (byte [] a) {กลับ IntStream.range (0, a.length) .mapToObj (i -> String.format ("% 02x ", [i])) .reduce ((acc, v) -> acc +" "+ v) .get (); }
ทิเบต

คำตอบ:


901

จากการอภิปรายที่นี่และโดยเฉพาะอย่างยิ่งนี้คำตอบนี้เป็นฟังก์ชั่นที่ผมใช้อยู่ในปัจจุบัน:

private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(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] = HEX_ARRAY[v >>> 4];
        hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
    }
    return new String(hexChars);
}

มาตรฐานเล็ก ๆ ของฉัน (ล้านไบต์ต่อพันครั้ง, 256 ไบต์ 10 ล้านครั้ง) แสดงให้เห็นว่ามันเร็วกว่าทางเลือกอื่น ๆ มากประมาณครึ่งเวลาในอาร์เรย์ที่ยาว เปรียบเทียบกับคำตอบที่ฉันเอามาเปลี่ยนเป็น bitwise ops ตามที่แนะนำในการอภิปรายลดเวลาประมาณ 20% สำหรับอาร์เรย์ยาว (แก้ไข: เมื่อฉันบอกว่ามันเร็วกว่าตัวเลือกอื่นฉันหมายถึงรหัสทางเลือกที่นำเสนอในการอภิปรายประสิทธิภาพเทียบเท่ากับ Commons Codec ซึ่งใช้รหัสที่คล้ายกันมาก)

รุ่น 2k20 ที่เกี่ยวข้องกับสตริงกระชับ Java 9:

private static final byte[] HEX_ARRAY = "0123456789ABCDEF".toByteArray();
public static String bytesToHex(byte[] bytes) {
    byte[] hexChars = new byte[bytes.length * 2];
    for (int j = 0; j < bytes.length; j++) {
        int v = bytes[j] & 0xFF;
        hexChars[j * 2] = HEX_ARRAY[v >>> 4];
        hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
    }
    return new String(hexChars, StandardCharsets.UTF_8);
}

266
ฉันเพิ่งพบjavax.xml.bind.DataTypeConverterซึ่งเป็นส่วนหนึ่งของการแจกแจงมาตรฐาน ทำไมสิ่งนี้ถึงไม่เกิดขึ้นเมื่อคุณใช้ Google ปัญหาแบบนี้? จำนวนมากที่เป็นประโยชน์เครื่องมือรวมถึงและ String printHexBinary(byte[]) อย่างไรก็ตามช้ากว่าฟังก์ชั่นในคำตอบนี้มาก (2x) (ฉันตรวจสอบแหล่งที่มามันใช้ a . ใช้อาร์เรย์) จริงๆแล้วสำหรับจุดประสงค์ส่วนใหญ่มันเร็วพอและคุณอาจมีอยู่แล้ว byte[] parseHexBinary(String)printHexBinarystringBuilderparseHexBinary
บางที WeCouldStealAVan

75
+1 สำหรับคำตอบเนื่องจาก Android ไม่มี DataTypeConverter
Vaiden

7
@maybeWeCouldStealAVan: JDK 7 เป็นแหล่งเปิด เราควรส่ง patch เพื่อปรับปรุงประสิทธิภาพprintHexBinaryใช่หรือไม่
kevinarpe

3
@maybeWeCouldStealAVan คุณช่วยอธิบายหน่อยได้ไหมว่ามันทำงานยังไง ฉันติดตามส่วนใหญ่ แต่ชอบเข้าใจสิ่งที่เกิดขึ้นเมื่อใช้รหัส ขอบคุณ!
jjNford

24
javax.xml.bind.DataTypeConverterกำลังถูกลบจาก Java 11
The Impaler

421

Apache Commons Codecห้องสมุดมีHexชั้นเรียนสำหรับการทำเพียงแค่ประเภทของงานนี้

import org.apache.commons.codec.binary.Hex;

String foo = "I am a string";
byte[] bytes = foo.getBytes();
System.out.println( Hex.encodeHexString( bytes ) );

12
@cytinus - downvote ของฉันเกิดขึ้นเมื่อ 4 เดือนที่แล้วดังนั้นฉันจึงไม่แน่ใจว่าฉันคิดอะไรอยู่ แต่ฉันก็ไม่เห็นด้วยกับขนาดของห้องสมุด นี่คือฟังก์ชั่นขนาดเล็กภายในโปรแกรม; ไม่จำเป็นต้องเพิ่มไลบรารีขนาดใหญ่ลงในโครงการเพื่อดำเนินการ
ArtOfWarfare

6
@ArtOfWarefare ฉันเห็นด้วยดังนั้นแทนที่จะimport org.apache.commons.codec.*;ทำเช่นนั้นได้import org.apache.commons.codec.binary.Hex;
cytinus

12
@ArtOfWarfare ฉันต้องไม่เห็นด้วย สิ่งเดียวที่น่ากลัวก็คือ apache คอมมอนส์ห้องสมุดไม่รวมอยู่ใน JRE และ JDK มีบางไลบรารีที่มีประโยชน์มาก ๆ พวกเขาควรจะอยู่ในพา ธ ของชั้นเรียนโดยปริยายและนี่คือหนึ่งในนั้น
corsiKa

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

6
หรือในกรณีที่คุณใช้ BouncyCastle ( org.bouncycastle: bcprov-jdk15on ) คุณสามารถใช้คลาสนี้: org.bouncycastle.util.encoders.Hexด้วยวิธีนี้:String toHexString(byte[] data)
Guillaume Husta

320

วิธีการjavax.xml.bind.DatatypeConverter.printHexBinary()ซึ่งเป็นส่วนหนึ่งของสถาปัตยกรรม Java สำหรับการผูก XML (JAXB)เป็นวิธีที่สะดวกในการแปลง a byte[]เป็นสตริงฐานสิบหก DatatypeConverterชั้นยังรวมถึงวิธีการอื่น ๆ อีกมากมายข้อมูลการจัดการที่มีประโยชน์

ใน Java 8 และก่อนหน้า JAXB เป็นส่วนหนึ่งของไลบรารีมาตรฐาน Java มันเลิกใช้แล้วด้วย Java 9 และลบออกด้วย Java 11ซึ่งเป็นส่วนหนึ่งของความพยายามที่จะย้ายแพ็คเกจ Java EE ทั้งหมดลงในไลบรารีของตนเอง มันเป็นเรื่องยาว ตอนนี้javax.xml.bindไม่มีอยู่และถ้าคุณต้องการใช้ JAXB ซึ่งมีอยู่DatatypeConverterคุณจะต้องติดตั้งJAXB APIและJAXB Runtimeจาก Maven

ตัวอย่างการใช้งาน:

byte bytes[] = {(byte)0, (byte)0, (byte)134, (byte)0, (byte)61};
String hex = javax.xml.bind.DatatypeConverter.printHexBinary(bytes);

จะส่งผลให้:

000086003D

คำตอบนี้เช่นเดียวกับคนนี้


13
ทางออกที่ดีแม้ว่าจะไม่ใช่เรื่องที่ถูกต้องใน Android
Kazriko

@Kazriko บางทีคุณอาจต้องการที่จะอ่านcode.google.com/p/dalvik/wiki/JavaxPackages มันเป็นวิธีการที่จะได้เรียน javax เป็น Android แต่ถ้าคุณต้องการแปลงเป็นเลขฐานสิบหกมันไม่คุ้มค่ากับปัญหา
PhoneixS

13
DatatypeConverter ไม่สามารถเข้าถึงได้อีกต่อไปตั้งแต่ JDK 9
pmcollins

3
@PhoneixS มันยังอยู่ที่นั่น แต่ไม่ได้เป็นส่วนหนึ่งของ runtime เริ่มต้น (เนื่องจากโมดูล Java 9)
Spotlight

2
อย่าพึ่งพา javax.xml.bind มันคอมไพล์ได้ดี แต่ไม่สามารถหาได้ตอนรันไทม์ หากคุณพร้อมที่จะจัดการกับ java.lang.NoClassDefFoundError
Dmitry

227

โซลูชันที่ง่ายที่สุดไม่มี libs ภายนอกไม่มีค่าคงที่ตัวเลข:

public static String byteArrayToHex(byte[] a) {
   StringBuilder sb = new StringBuilder(a.length * 2);
   for(byte b: a)
      sb.append(String.format("%02x", b));
   return sb.toString();
}

14
สิ่งนี้ช้ามากโดยเฉลี่ย 1,000 ครั้งช้ากว่า (สำหรับความยาว 162 ไบต์) ซึ่งสูงกว่าการตอบกลับยอดนิยม หลีกเลี่ยงการใช้ String จัดรูปแบบถ้าเรื่องประสิทธิภาพ
pt123

8
อาจจะช้า เหมาะสำหรับสิ่งที่เกิดขึ้นเป็นครั้งคราวเช่นเข้าสู่ระบบหรือคล้ายกัน
ตัวชี้ Null

29
ถ้ามันช้าอะไรนะ? ในกรณีของฉันใช้มันเป็นเพียงคำสั่งการแก้ปัญหาดังนั้นขอขอบคุณสำหรับส่วนของรหัสนี้
สแกนดิเนเวียน

8
การนำไลบรารี่กลับมาใช้ใหม่โดยการรวมไฟล์ JAR หลายสิบ kB จะไม่ได้ผลถ้าคุณต้องการเพียงแค่ฟังก์ชั่นนี้ (ในบางแพลตฟอร์มเช่น Android Jar ทั้งหมดจะรวมอยู่ในแอพพลิเคชั่นสุดท้าย) และบางครั้งรหัสที่สั้นกว่าและชัดเจนกว่าจะดีกว่าเมื่อไม่ต้องการประสิทธิภาพ
personne3000

2
อาจ @ personne3000 แต่ในกรณีนี้คุณต้องการการสนับสนุนแบบสตรีมไม่ใช่คุณลักษณะการโทรเดียว อันนี้ง่ายต่อการเข้าใจและจดจำและดังนั้นเพื่อรักษา
Maarten Bodewes

59

โซลูชัน Guava เพื่อความสมบูรณ์:

import com.google.common.io.BaseEncoding;
...
byte[] bytes = "Hello world".getBytes(StandardCharsets.UTF_8);
final String hex = BaseEncoding.base16().lowerCase().encode(bytes);

ตอนนี้คือhex"48656c6c6f20776f726c64"


new HashCode(bytes).toString()ในฝรั่งคุณยังสามารถใช้
mfulton26

1
ในฐานะของ Guava 22.0 มันเป็นHashCode.fromBytes(checksum).toString()
Devstr

43

oneliner ง่าย ๆ นี้ทำงานให้ฉัน
String result = new BigInteger(1, inputBytes).toString(16);
แก้ไข - การใช้สิ่งนี้จะลบศูนย์นำหน้า แต่เฮ้ใช้ได้ผลกับกรณีการใช้งานของฉัน ขอบคุณ @Voicu ที่ชี้ให้เห็น


56
Onelinerนี้จะนำหน้าศูนย์ไบต์
Voicu

@Voicu ... และมันจะเพิ่มศูนย์นำหน้า 50% ของเวลา
Maarten Bodewes

27

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

ตัวเลือกที่ 1: ข้อมูลโค้ด - ง่าย

วิธีแก้ปัญหาที่ง่ายมากวิธีหนึ่งคือใช้การBigIntegerแสดงเลขฐานสิบหกของ:

new BigInteger(1, someByteArray).toString(16)

โปรดทราบว่าตั้งแต่นี้จะจัดการกับตัวเลขที่ไม่ได้เป็นสตริงไบต์โดยพลการมันจะละเว้นศูนย์นำหน้า - นี่อาจหรือไม่อาจเป็นสิ่งที่คุณต้องการ (เช่น000AE3vs 0AE3สำหรับการป้อนข้อมูล 3 ไบต์) นี่ก็ช้ามากประมาณ100x ช้ากว่าเมื่อเทียบกับตัวเลือกถัดไป

ตัวเลือก 2: ข้อมูลโค้ด - ขั้นสูง

ที่นี่เป็นที่โดดเด่นเต็มคัดลอกและรหัส pasteable snippet สนับสนุนบน / ตัวพิมพ์เล็กและendianness มันได้รับการปรับให้เหมาะสมเพื่อลดความซับซ้อนของหน่วยความจำและเพิ่มประสิทธิภาพการทำงานและควรเข้ากันได้กับ Java รุ่นใหม่ทุกรุ่น (5+)

private static final char[] LOOKUP_TABLE_LOWER = new char[]{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66};
private static final char[] LOOKUP_TABLE_UPPER = new char[]{0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46};

public static String encode(byte[] byteArray, boolean upperCase, ByteOrder byteOrder) {

    // our output size will be exactly 2x byte-array length
    final char[] buffer = new char[byteArray.length * 2];

    // choose lower or uppercase lookup table
    final char[] lookup = upperCase ? LOOKUP_TABLE_UPPER : LOOKUP_TABLE_LOWER;

    int index;
    for (int i = 0; i < byteArray.length; i++) {
        // for little endian we count from last to first
        index = (byteOrder == ByteOrder.BIG_ENDIAN) ? i : byteArray.length - i - 1;

        // extract the upper 4 bit and look up char (0-A)
        buffer[i << 1] = lookup[(byteArray[index] >> 4) & 0xF];
        // extract the lower 4 bit and look up char (0-A)
        buffer[(i << 1) + 1] = lookup[(byteArray[index] & 0xF)];
    }
    return new String(buffer);
}

public static String encode(byte[] byteArray) {
    return encode(byteArray, false, ByteOrder.BIG_ENDIAN);
}

รหัสที่มาเต็มกับ Apache ใบอนุญาต v2 และถอดรหัสสามารถพบได้ที่นี่

ตัวเลือก 3: การใช้ไลบรารีที่ปรับให้เหมาะสมขนาดเล็ก: bytes-java

ในขณะที่ทำงานในโครงการก่อนหน้าของฉันฉันสร้างชุดเครื่องมือเล็ก ๆ นี้สำหรับทำงานกับไบต์ใน Java ไม่มีการอ้างอิงภายนอกและเข้ากันได้กับ Java 7+ ซึ่งรวมถึง HEX en / decoder ที่รวดเร็วและผ่านการทดสอบอย่างดี:

import at.favre.lib.bytes.Bytes;
...
Bytes.wrap(someByteArray).encodeHex()

คุณสามารถตรวจสอบมันออกมาบนGithub: ไบต์

ตัวเลือก 4: Apache Commons Codec

แน่นอนมีดี 'OL คอมมอนตัวแปลงสัญญาณ ( ความเห็นคำเตือนล่วงหน้า ) ในขณะที่ทำงานในโครงการที่ระบุไว้ข้างต้นฉันวิเคราะห์รหัสและค่อนข้างผิดหวัง; รหัสที่ไม่มีการรวบรวมกันจำนวนมากตัวแปลงสัญญาณที่ล้าสมัยและแปลกใหม่อาจมีประโยชน์สำหรับการใช้งานตัวแปลงสัญญาณยอดนิยมน้อยมากและค่อนข้างช้ากว่าการใช้งานเชิงวิศวกรรมและแบบช้า (เฉพาะ Base64) ฉันจะตัดสินใจอย่างรอบคอบหากคุณต้องการใช้หรือเป็นทางเลือก อย่างไรก็ตามหากคุณยังต้องการใช้งานต่อไปนี้เป็นข้อมูลโค้ด:

import org.apache.commons.codec.binary.Hex;
...
Hex.encodeHexString(someByteArray));

ตัวเลือกที่ 5: Google Guava

บ่อยกว่าที่คุณไม่ได้มีฝรั่งเป็นที่พึ่ง ถ้าเป็นเช่นนั้นเพียงใช้:

import com.google.common.io.BaseEncoding;
...
BaseEncoding.base16().lowerCase().encode(someByteArray);

ตัวเลือก 6: Spring Security

หากคุณใช้Spring Frameworkด้วยSpring Securityคุณสามารถใช้สิ่งต่อไปนี้:

import org.springframework.security.crypto.codec.Hex
...
new String(Hex.encode(someByteArray));

ตัวเลือกที่ 7: ปราสาท Bouncy

หากคุณใช้กรอบความปลอดภัยBouncy Castle อยู่แล้วคุณสามารถใช้ประโยชน์ของมันได้Hex:

import org.bouncycastle.util.encoders.Hex;
...
Hex.toHexString(someByteArray);

ไม่ใช่ตัวเลือกที่ 8: ความเข้ากันได้ของ Java 9+ หรือ 'ห้ามใช้ JAXBs javax / xml / bind / DatatypeConverter'

ใน Java เวอร์ชันก่อนหน้า (8 และต่ำกว่า) โค้ด Java สำหรับ JAXB ถูกรวมเป็นการอ้างอิงแบบรันไทม์ ตั้งแต่ Java 9 และJigsawis ​​modularisationรหัสของคุณไม่สามารถเข้าถึงรหัสอื่นนอกโมดูลได้โดยไม่ต้องประกาศอย่างชัดเจน ดังนั้นระวังหากคุณได้รับข้อยกเว้นเช่น:

java.lang.NoClassDefFoundError: javax/xml/bind/JAXBException

เมื่อทำงานกับ JVM ด้วย Java 9+ หากเป็นเช่นนั้นให้สลับการใช้งานไปยังตัวเลือกใด ๆ ด้านบน ดูคำถามนี้ด้วย


เกณฑ์มาตรฐานไมโคร

นี่คือผลที่ได้จากการที่ง่ายJMHมาตรฐานการเข้ารหัสไมโครอาร์เรย์ไบต์ขนาดแตกต่างกัน ค่าคือการดำเนินการต่อวินาทีดังนั้นสูงกว่าดีกว่า โปรดทราบว่าการวัดขนาดเล็กมักจะไม่ได้แสดงถึงพฤติกรรมในโลกแห่งความเป็นจริงดังนั้นให้นำผลลัพธ์เหล่านี้ไปด้วยเกลือ

| Name (ops/s)         |    16 byte |    32 byte |  128 byte | 0.95 MB |
|----------------------|-----------:|-----------:|----------:|--------:|
| Opt1: BigInteger     |  2,088,514 |  1,008,357 |   133,665 |       4 |
| Opt2/3: Bytes Lib    | 20,423,170 | 16,049,841 | 6,685,522 |     825 |
| Opt4: Apache Commons | 17,503,857 | 12,382,018 | 4,319,898 |     529 |
| Opt5: Guava          | 10,177,925 |  6,937,833 | 2,094,658 |     257 |
| Opt6: Spring         | 18,704,986 | 13,643,374 | 4,904,805 |     601 |
| Opt7: BC             |  7,501,666 |  3,674,422 | 1,077,236 |     152 |
| Opt8: JAX-B          | 13,497,736 |  8,312,834 | 2,590,940 |     346 |

รายละเอียด: JDK 8u202, i7-7700K, Win10, 24GB Ram ดูมาตรฐานเต็มรูปแบบที่นี่



21

ฉันจะใช้สิ่งนี้เพื่อความยาวคงที่เช่นแฮช:

md5sum = String.format("%032x", new BigInteger(1, md.digest()));

2
ขอบคุณนี่เป็นสาระสำคัญและเหมาะสม
Deepan Prabhu Babu

17

ฉันพบวิธีที่แตกต่างกันสามวิธีที่นี่: http://www.rgagnon.com/javadetails/java-0596.html

อันที่สวยที่สุดในขณะที่เขายังบันทึกฉันคิดว่าเป็นคนนี้:

static final String HEXES = "0123456789ABCDEF";
public static String getHex( byte [] raw ) {
    if ( raw == null ) {
        return null;
    }
    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();
}

วิธีอื่น ๆ กำลังทำงานกับตัวอย่าง 64 byte ของฉันใน 5ms หนึ่งนี้ทำงานใน 0ms อาจดีที่สุดสำหรับการขาดฟังก์ชั่นอื่น ๆ เช่นสตริงรูปแบบ
Joseph Lust

if (raw == null) return nullไม่ได้ล้มเหลวอย่างรวดเร็ว ทำไมคุณถึงเคยใช้nullกุญแจ
Maarten Bodewes

ฉันคิดว่ามันเป็นนิสัยในการตรวจสอบการป้อนข้อมูล ในกรณีนี้เราป้องกันข้อยกเว้นการอ้างอิงใด ๆ ของ Null และปล่อยให้ผู้เรียกจัดการข้อมูลที่ไม่ดี
Michael Bisbjerg

16

ด้วยค่าใช้จ่ายเล็กน้อยในการจัดเก็บตารางการค้นหาการใช้งานนี้ง่ายและรวดเร็วมาก

 private static final char[] BYTE2HEX=(
    "000102030405060708090A0B0C0D0E0F"+
    "101112131415161718191A1B1C1D1E1F"+
    "202122232425262728292A2B2C2D2E2F"+
    "303132333435363738393A3B3C3D3E3F"+
    "404142434445464748494A4B4C4D4E4F"+
    "505152535455565758595A5B5C5D5E5F"+
    "606162636465666768696A6B6C6D6E6F"+
    "707172737475767778797A7B7C7D7E7F"+
    "808182838485868788898A8B8C8D8E8F"+
    "909192939495969798999A9B9C9D9E9F"+
    "A0A1A2A3A4A5A6A7A8A9AAABACADAEAF"+
    "B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF"+
    "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF"+
    "D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF"+
    "E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF"+
    "F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF").toCharArray();
   ; 

  public static String getHexString(byte[] bytes) {
    final int len=bytes.length;
    final char[] chars=new char[len<<1];
    int hexIndex;
    int idx=0;
    int ofs=0;
    while (ofs<len) {
      hexIndex=(bytes[ofs++] & 0xFF)<<1;
      chars[idx++]=BYTE2HEX[hexIndex++];
      chars[idx++]=BYTE2HEX[hexIndex];
    }
    return new String(chars);
  }

6
ทำไมไม่เริ่มต้นBYTE2HEXอาร์เรย์ด้วยforวงจรอย่างง่าย?
icza

@icza นั่นเป็นไปได้หรือไม่ที่มีเขตข้อมูลสุดท้าย (คงที่ aka) หรือไม่
เนวิลิส

1
@nevelis มันสามารถกำหนดได้ในstatic { }บล็อก
マルちゃんだよ

1
@icza เพราะมันเร็วกว่าการ hardcode ตารางการค้นหามากกว่าที่จะสร้างมัน นี่คือความซับซ้อนของหน่วยความจำมีการแลกเปลี่ยนกับความซับซ้อนของเวลาคือ ต้องการหน่วยความจำมากขึ้น แต่เร็วขึ้น (ทุก ๆ ด้านทั้งสองข้าง)
Patrick Favre

8

แล้วเรื่องนี้ล่ะ

    String byteToHex(final byte[] hash)
    {
        Formatter formatter = new Formatter();
        for (byte b : hash)
        {
            formatter.format("%02x", b);
        }
        String result = formatter.toString();
        formatter.close();
        return result;
    }

3

เราไม่จำเป็นต้องใช้ไลบรารีภายนอกหรือเขียนโค้ดตามลูปและค่าคงที่
แค่นี้ก็พอ:

byte[] theValue = .....
String hexaString = new BigInteger(1, theValue).toString(16);

1
นี้คล้ายกันมากกับคำตอบที่เคยมั่นใจซื้อของ
Scratte

2

ฉันชอบที่จะใช้สิ่งนี้:

final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
public static String bytesToHex(byte[] bytes, int offset, int count) {
    char[] hexChars = new char[count * 2];
    for ( int j = 0; j < count; j++ ) {
        int v = bytes[j+offset] & 0xFF;
        hexChars[j * 2] = hexArray[v >>> 4];
        hexChars[j * 2 + 1] = hexArray[v & 0x0F];
    }
    return new String(hexChars);
}

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


คำถามเดิมมีไว้สำหรับไบต์ [] ถึงสตริง ดูเลขฐานสิบหกเป็นไบต์ [] หรือถามคำถามอื่น @NonExistent
Bamaco

2

ฉันมักจะใช้วิธีการต่อไปนี้สำหรับคำสั่ง debuf แต่ฉันไม่รู้ว่ามันเป็นวิธีที่ดีที่สุดในการทำมันหรือไม่

private static String digits = "0123456789abcdef";

public static String toHex(byte[] data){
    StringBuffer buf = new StringBuffer();
    for (int i = 0; i != data.length; i++)
    {
        int v = data[i] & 0xff;
        buf.append(digits.charAt(v >> 4));
        buf.append(digits.charAt(v & 0xf));
    }
    return buf.toString();
}

2
หาก debuffer ของคุณมีวันที่ไม่ดีลอง cluing ใน StringBuilder instantiation StringBuilder buf = new StringBuilder(data.length * 2);กับจำนวนของตัวอักษรที่ให้การสนับสนุน:
greybeard

2

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

org.apache.commons.codec.binary.Hex

บางทีคุณอาจจะมี ...

org.apache.xerces.impl.dv.util.HexBin


2

หากคุณใช้เฟรมเวิร์ก Spring Security คุณสามารถใช้:

import org.springframework.security.crypto.codec.Hex

final String testString = "Test String";
final byte[] byteArray = testString.getBytes();
System.out.println(Hex.encode(byteArray));

2

การเพิ่มยูทิลิตี้ jar สำหรับฟังก์ชั่นง่าย ๆ ไม่ใช่ตัวเลือกที่ดี รวบรวมคลาสยูทิลิตี้ของคุณเองแทน ต่อไปนี้เป็นไปได้เร็วขึ้นการใช้งาน

public class ByteHex {

    public static int hexToByte(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 String[] byteToHexTable = 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"
    };

    private static final String[] byteToHexTableLowerCase = 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 byteToHexTable[b & 0xFF];
    }

    public static String byteToHex(byte[] bytes){
        if(bytes == null) return null;
        StringBuilder sb = new StringBuilder(bytes.length*2);
        for(byte b : bytes) sb.append(byteToHexTable[b & 0xFF]);
        return sb.toString();
    }

    public static String byteToHex(short[] bytes){
        StringBuilder sb = new StringBuilder(bytes.length*2);
        for(short b : bytes) sb.append(byteToHexTable[((byte)b) & 0xFF]);
        return sb.toString();
    }

    public static String byteToHexLowerCase(byte[] bytes){
        StringBuilder sb = new StringBuilder(bytes.length*2);
        for(byte b : bytes) sb.append(byteToHexTableLowerCase[b & 0xFF]);
        return sb.toString();
    }

    public static byte[] hexToByte(String hexString) {
        if(hexString == null) return null;
        byte[] byteArray = new byte[hexString.length() / 2];
        for (int i = 0; i < hexString.length(); i += 2) {
            byteArray[i / 2] = (byte) (hexToByte(hexString.charAt(i)) * 16 + hexToByte(hexString.charAt(i+1)));
        }
        return byteArray;
    }

    public static byte hexPairToByte(char ch1, char ch2) {
        return (byte) (hexToByte(ch1) * 16 + hexToByte(ch2));
    }


}

1

ชุดตัวเลือกขนาดเล็กของโซลูชันที่เสนอโดย @maybewecouldstealavan ซึ่งช่วยให้คุณสามารถรวม N ไบต์เข้าด้วยกันในสตริง hex ของเอาต์พุต:

 final static char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
 final static char BUNDLE_SEP = ' ';

public static String bytesToHexString(byte[] bytes, int bundleSize /*[bytes]*/]) {
        char[] hexChars = new char[(bytes.length * 2) + (bytes.length / bundleSize)];
        for (int j = 0, k = 1; j < bytes.length; j++, k++) {
                int v = bytes[j] & 0xFF;
                int start = (j * 2) + j/bundleSize;

                hexChars[start] = HEX_ARRAY[v >>> 4];
                hexChars[start + 1] = HEX_ARRAY[v & 0x0F];

                if ((k % bundleSize) == 0) {
                        hexChars[start + 2] = BUNDLE_SEP;
                }   
        }   
        return new String(hexChars).trim();    
}

นั่นคือ:

bytesToHexString("..DOOM..".toCharArray().getBytes(), 2);
2E2E 444F 4F4D 2E2E

bytesToHexString("..DOOM..".toCharArray().getBytes(), 4);
2E2E444F 4F4D2E2E

1

ไม่พบวิธีการแก้ปัญหาใด ๆ ในหน้านี้ที่ไม่ได้

  1. ใช้วง
  2. ใช้ javax.xml.bind.DatatypeConverter ซึ่งคอมไพล์ได้ดี แต่มักจะพ่น java.lang.NoClassDefFoundError ตอนรันไทม์

นี่คือวิธีการแก้ปัญหาที่ไม่มีข้อบกพร่องด้านบน (ไม่มีสัญญาว่าฉันจะไม่มีข้อบกพร่องอื่น ๆ )

import java.math.BigInteger;

import static java.lang.System.out;
public final class App2 {
    // | proposed solution.
    public static String encode(byte[] bytes) {          
        final int length = bytes.length;

        // | BigInteger constructor throws if it is given an empty array.
        if (length == 0) {
            return "00";
        }

        final int evenLength = (int)(2 * Math.ceil(length / 2.0));
        final String format = "%0" + evenLength + "x";         
        final String result = String.format (format, new BigInteger(bytes));

        return result;
    }

    public static void main(String[] args) throws Exception {
        // 00
        out.println(encode(new byte[] {})); 

        // 01
        out.println(encode(new byte[] {1})); 

        //203040
        out.println(encode(new byte[] {0x20, 0x30, 0x40})); 

        // 416c6c20796f75722062617365206172652062656c6f6e6720746f2075732e
        out.println(encode("All your base are belong to us.".getBytes()));
    }
}   

ฉันไม่สามารถรับสิ่งนี้ได้ภายใต้ 62 opcodes แต่ถ้าคุณสามารถอยู่ได้โดยไม่มีการเว้นระยะห่าง 0 ในกรณีที่ไบต์แรกน้อยกว่า 0x10 วิธีการแก้ปัญหาต่อไปนี้จะใช้ 23 opcodes เท่านั้น แสดงวิธีแก้ปัญหา "ใช้งานง่ายด้วยตัวคุณเอง" เช่น "แผ่นที่มีศูนย์หากความยาวของสตริงเป็นเลขคี่" อาจมีราคาแพงมากหากการใช้งานดั้งเดิมไม่พร้อมใช้งาน (หรือในกรณีนี้ถ้า BigInteger มีตัวเลือก toString)

public static String encode(byte[] bytes) {          
    final int length = bytes.length;

    // | BigInteger constructor throws if it is given an empty array.
    if (length == 0) {
        return "00";
    }

    return new BigInteger(bytes).toString(16);
}

1

โซลูชันของฉันขึ้นอยู่กับโซลูชันของ WeWeCouldStealAVan แต่ไม่พึ่งพาตารางการค้นหาที่ได้รับการจัดสรรเพิ่มเติม มันไม่ได้ใช้การแฮ็ก 'int-to-char' (จริง ๆ แล้วCharacter.forDigit()มันทำการเปรียบเทียบบางอย่างเพื่อตรวจสอบว่าตัวเลขแท้จริงคืออะไร) และดังนั้นจึงอาจช้าลงเล็กน้อย โปรดใช้ทุกที่ที่คุณต้องการ ไชโย

public static String bytesToHex(final byte[] bytes)
{
    final int numBytes = bytes.length;
    final char[] container = new char[numBytes * 2];

    for (int i = 0; i < numBytes; i++)
    {
        final int b = bytes[i] & 0xFF;

        container[i * 2] = Character.forDigit(b >>> 4, 0x10);
        container[i * 2 + 1] = Character.forDigit(b & 0xF, 0x10);
    }

    return new String(container);
}

0

// การเปลี่ยนไบต์มีประสิทธิภาพมากขึ้น // คุณสามารถใช้สิ่งนี้ได้เช่นกัน

public static String getHexString (String s) 
{
    byte[] buf = s.getBytes();

    StringBuffer sb = new StringBuffer();

    for (byte b:buf)
    {
        sb.append(String.format("%x", b));
    }


        return sb.toString();
}

0

หากคุณกำลังมองหาอาร์เรย์แบบตรงนี้สำหรับไพ ธ อนฉันได้แปลงการใช้จาวานี้ไปเป็นไพ ธ อน

class ByteArray:

@classmethod
def char(cls, args=[]):
    cls.hexArray = "0123456789ABCDEF".encode('utf-16')
    j = 0
    length = (cls.hexArray)

    if j < length:
        v = j & 0xFF
        hexChars = [None, None]
        hexChars[j * 2] = str( cls.hexArray) + str(v)
        hexChars[j * 2 + 1] = str(cls.hexArray) + str(v) + str(0x0F)
        # Use if you want...
        #hexChars.pop()

    return str(hexChars)

array = ByteArray()
print array.char(args=[])

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

0

นี่คือการjava.util.Base64ใช้งานที่คล้ายกัน (บางส่วน) ไม่สวยใช่ไหม

public class Base16/*a.k.a. Hex*/ {
    public static class Encoder{
        private static char[] toLowerHex={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
        private static char[] toUpperHex={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
        private boolean upper;
        public Encoder(boolean upper) {
            this.upper=upper;
        }
        public String encode(byte[] data){
            char[] value=new char[data.length*2];
            char[] toHex=upper?toUpperHex:toLowerHex;
            for(int i=0,j=0;i<data.length;i++){
                int octet=data[i]&0xFF;
                value[j++]=toHex[octet>>4];
                value[j++]=toHex[octet&0xF];
            }
            return new String(value);
        }
        static final Encoder LOWER=new Encoder(false);
        static final Encoder UPPER=new Encoder(true);
    }
    public static Encoder getEncoder(){
        return Encoder.LOWER;
    }
    public static Encoder getUpperEncoder(){
        return Encoder.UPPER;
    }
    //...
}

0
private static String bytesToHexString(byte[] bytes, int length) {
        if (bytes == null || length == 0) return null;

        StringBuilder ret = new StringBuilder(2*length);

        for (int i = 0 ; i < length ; i++) {
            int b;

            b = 0x0f & (bytes[i] >> 4);
            ret.append("0123456789abcdef".charAt(b));

            b = 0x0f & bytes[i];
            ret.append("0123456789abcdef".charAt(b));
        }

        return ret.toString();
    }

0
Converts bytes data to hex characters

@param bytes byte array to be converted to hex string
@return byte String in hex format

private static String bytesToHex(byte[] bytes) {
    char[] hexChars = new char[bytes.length * 2];
    int v;
    for (int j = 0; j < bytes.length; j++) {
        v = bytes[j] & 0xFF;
        hexChars[j * 2] = HEX_ARRAY[v >>> 4];
        hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F];
    }
    return new String(hexChars);
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.