Mã Java Để chuyển đổi byte thành Hệ thập lục phân


184

Tôi có một mảng byte. Tôi muốn mỗi chuỗi byte của mảng đó được chuyển đổi thành các giá trị thập lục phân tương ứng của nó.

Có bất kỳ chức năng nào trong Java để chuyển đổi một mảng byte thành Hexadecimal không?


2
Những gì bạn gọi là mảng byte trong Java được gọi là chuỗi byte trong các ngôn ngữ khác (ví dụ: docs.racket-lang.org/guide/bytestrings.html )
Patrick Favre

Câu trả lời:


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 "

Xem thêm

  • java.util.Formatter cú pháp
    • %[flags][width]conversion
      • Cờ '0'- Kết quả sẽ là không đệm
      • Chiều rộng 2
      • Chuyển đổi 'X'- Kết quả được định dạng dưới dạng số nguyên thập lục phân, chữ hoa

Nhìn vào văn bản của câu hỏi, cũng có thể đây là những gì được yêu cầu:

    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]"

Một số câu trả lời ở đây sử dụng Integer.toHexString(int); Điều này là có thể làm được, nhưng với một số hãy cẩn thận. Vì tham số là một int, một chuyển đổi nguyên thủy mở rộng được thực hiện cho byteđối số, bao gồm mở rộng dấu hiệu.

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

8 bit byte, được ký trong Java, được mở rộng đăng nhập thành 32 bit int. Để hoàn tác hiệu quả phần mở rộng dấu hiệu này, người ta có thể che dấu bytevới 0xFF.

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

Một vấn đề khác khi sử dụng toHexStringlà nó không đệm với số không:

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

Cả hai yếu tố kết hợp nên làm cho String.formatgiải pháp thích hợp hơn.

Người giới thiệu


@Vivek: "giá trị rất lớn" là gì? Đầu vào là gì và đầu ra là gì?
đa gen

Hãy để tôi giải thích lại .. Tôi có một tập hợp các chuỗi byte trong một mảng. Nhưng những gì tôi phải làm là phân tích từng byte một cách riêng biệt .. Vì vậy, tôi không muốn làm việc trên toàn bộ mảng, nhưng một chuỗi byte riêng lẻ tại một thời điểm, đó là một thành phần của mảng đó .. Sự nhầm lẫn nảy sinh do từ " mảng". Bây giờ trong đoạn mã dưới đây "byte bv = 10; String hexString = Integer.toHexString (bv);" CAse 1 (Byte Recpered: 68 Hex Đầu ra :: 44) Trường hợp: 2 (Byte Nhận được: -46 Đầu ra Hex :: ffffffd2) ......... Tại sao tôi nhận được một kết quả bất ngờ như vậy cho một số giá trị?
Vivek

1
@Vivek: đọc câu trả lời của tôi về việc sử dụng toHexString. Bạn phải che nó với & 0xFF, tức Integer.toHexString(-46 & 0xFF)"d2".
đa gen

@polygenelubricants: Thanx rất nhiều .. Có vẻ như, cuối cùng mã cũng hoạt động tốt .. Có an toàn khi sử dụng chức năng toHexString bây giờ không? Hoặc, có thể có một số sơ hở với cách tiếp cận?
Vivek

1
@Vivek: nó "an toàn", bạn chỉ cần cẩn thận và đảm bảo rằng bạn luôn che dấu bytegiá trị & 0xFF. các formatgiải pháp trên cũng có thể yêu cầu mặt nạ tùy thuộc vào những gì bạn đang thực sự sử dụng như là đối số.
đa gen

65

Tôi đang đăng bài vì không có câu trả lời nào giải thích được tại sao cách tiếp cận của họ hoạt động, điều mà tôi nghĩ là thực sự quan trọng cho vấn đề này. Trong một số trường hợp, điều này làm cho giải pháp đề xuất xuất hiện phức tạp không cần thiết và tinh tế. Để minh họa tôi sẽ cung cấp một cách tiếp cận khá đơn giản, nhưng tôi sẽ cung cấp thêm một chút chi tiết để giúp minh họa tại sao nó hoạt động.

Trước hết, chúng ta đang cố gắng làm gì? Chúng tôi muốn chuyển đổi một giá trị byte (hoặc một mảng byte) thành một chuỗi đại diện cho một giá trị thập lục phân trong ASCII. Vì vậy, bước một là tìm ra chính xác một byte trong Java là gì:

Kiểu dữ liệu byte là một số nguyên bổ sung có hai chữ ký 8 bit . Nó có giá trị tối thiểu là -128 và giá trị tối đa là 127 (đã bao gồm). Kiểu dữ liệu byte có thể hữu ích để lưu bộ nhớ trong các mảng lớn, trong đó việc tiết kiệm bộ nhớ thực sự quan trọng. Chúng cũng có thể được sử dụng thay cho int nơi giới hạn của chúng giúp làm rõ mã của bạn; thực tế là phạm vi của một biến bị giới hạn có thể đóng vai trò là một dạng tài liệu.

Điều đó có nghĩa là gì? Một vài điều: Đầu tiên và quan trọng nhất, nó có nghĩa là chúng tôi đang làm việc với 8 bit . Vì vậy, ví dụ chúng ta có thể viết số 2 là 0000 0010. Tuy nhiên, vì nó là phần bù của hai, nên chúng ta viết số 2 âm như thế này: 1111 1110. Điều gì cũng có nghĩa là việc chuyển đổi sang hex rất đơn giản. Đó là, bạn chỉ cần chuyển đổi trực tiếp từng phân đoạn 4 bit thành hex. Lưu ý rằng để hiểu được các số âm trong sơ đồ này, trước tiên bạn sẽ cần hiểu phần bù của hai. Nếu bạn chưa hiểu phần bổ sung của hai, bạn có thể đọc một lời giải thích tuyệt vời, tại đây: http://www.cs.cornell.edu/~tomf/notes/cps104/twoscomp.html


Chuyển đổi bổ sung của hai thành Hex nói chung

Khi một số nằm trong phần bù của hai, việc chuyển đổi nó thành hex là rất đơn giản. Nói chung, việc chuyển đổi từ nhị phân sang hex rất đơn giản và như bạn sẽ thấy trong hai ví dụ tiếp theo, bạn có thể chuyển trực tiếp từ hai bổ sung sang hex.

Ví dụ

Ví dụ 1: Chuyển đổi 2 thành Hex.

1) Đầu tiên chuyển đổi 2 thành nhị phân trong phần bù của hai:

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

2) Bây giờ chuyển đổi nhị phân thành hex:

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

therefore 2 = 0000 0010 = 0x02. 

Ví dụ 2: Chuyển đổi -2 (bổ sung hai) thành Hex.

1) Đầu tiên chuyển đổi -2 thành nhị phân trong phần bù của hai:

-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) Bây giờ Chuyển đổi sang Hex:

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

therefore: -2 = 1111 1110 = 0xFE.


Làm điều này trong Java

Bây giờ chúng tôi đã trình bày khái niệm này, bạn sẽ thấy chúng tôi có thể đạt được những gì chúng tôi muốn với một số mặt nạ và dịch chuyển đơn giản. Điều quan trọng cần hiểu là byte mà bạn đang cố gắng chuyển đổi đã có trong phần bù hai. Bạn không tự thực hiện việc chuyển đổi này. Tôi nghĩ rằng đây là một điểm chính của sự nhầm lẫn về vấn đề này. Lấy ví dụ mảng byte sau:

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

Chúng tôi chỉ tự chuyển đổi chúng thành hex, ở trên, nhưng làm thế nào chúng tôi có thể làm điều đó trong Java? Đây là cách thực hiện:

Bước 1: Tạo StringBuffer để giữ tính toán của chúng tôi.

StringBuffer buffer = new StringBuffer();

Bước 2: Cô lập các bit bậc cao hơn, chuyển đổi chúng thành hex và nối chúng vào bộ đệm

Với số nhị phân 1111 1110, chúng ta có thể cô lập các bit bậc cao hơn bằng cách trước tiên chuyển chúng qua 4, và sau đó loại bỏ phần còn lại của số. Về mặt logic, điều này là đơn giản, tuy nhiên, các chi tiết triển khai trong Java (và nhiều ngôn ngữ) giới thiệu một nếp nhăn vì phần mở rộng dấu hiệu. Về cơ bản, khi bạn thay đổi một giá trị byte, trước tiên Java sẽ chuyển đổi giá trị của bạn thành một số nguyên và sau đó thực hiện mở rộng dấu hiệu. Vì vậy, trong khi bạn mong đợi 1111 1110 >> 4 là 0000 1111, trong thực tế, trong Java, nó được biểu diễn dưới dạng bổ sung 0xFFFFFFFF của hai!

Vì vậy, trở lại ví dụ của chúng tôi:

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

Sau đó chúng ta có thể cô lập các bit bằng mặt nạ:

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

Trong Java, chúng ta có thể làm tất cả điều này trong một lần:

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

Hàm forDigit chỉ ánh xạ số bạn chuyển nó vào tập hợp các số thập lục phân 0-F.

Bước 3: Tiếp theo chúng ta cần cách ly các bit thứ tự thấp hơn. Vì các bit mà chúng ta muốn đã ở đúng vị trí, chúng ta chỉ có thể che giấu chúng:

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

Giống như trước đây, trong Java, chúng ta có thể thực hiện tất cả điều này trong một lần:

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

Đặt tất cả những thứ này lại với nhau, chúng ta có thể thực hiện nó như một vòng lặp for và chuyển đổi toàn bộ mảng:

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

Hy vọng rằng lời giải thích này làm cho mọi thứ rõ ràng hơn cho những người bạn tự hỏi chính xác những gì đang xảy ra trong nhiều ví dụ bạn sẽ tìm thấy trên internet. Hy vọng rằng tôi đã không mắc phải bất kỳ lỗi nghiêm trọng nào, nhưng các đề xuất và chỉnh sửa rất đáng hoan nghênh!


4
câu trả lời tốt nhất! Việc triển khai đối xứng của chuỗi hex thành byte sẽ hội tụ sau đó sử dụng Character.digit(), như(byte) ((Character.digit(str.charAt(0), 16) << 4) + Character.digit(str.charAt(1), 16))
ericbn

21

Cách nhanh nhất tôi đã tìm thấy để làm điều này là như sau:

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

Nó nhanh hơn ~ 50 lần String.format. nếu bạn muốn kiểm tra nó:

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
        }

    }
}

Chỉnh sửa : Chỉ tìm thấy thứ gì đó nhanh hơn một chút và giữ trên một dòng nhưng không tương thích với JRE 9. Sử dụng rủi ro của riêng bạn

import javax.xml.bind.DatatypeConverter;

DatatypeConverter.printHexBinary(raw);

2
DatatypeConverter không còn có sẵn trong Java 9. Điều nguy hiểm là mã sử dụng nó sẽ biên dịch theo Java 1.8 trở về trước (Java 9 với các cài đặt nguồn thành trước đó), nhưng có một ngoại lệ thời gian chạy trong Java 9.
Stephen M -on strike-

Tôi thứ hai @StephenMs điểm: sử dụng cái này với jre9 sẽ gặp sự cố với ClassNotFound Exception
Patrick Favre

Trên thực tế có thể chỉ cần trích xuất phương thức mã nguồn printHexBinary từ src.zip của jdk, dường như nhanh hơn 1 lần so với phương thức 1.
Trái cây

1
Nếu bạn làm việc với mảng char cho hằng số HEXES, thay vào đó là String và charAt (), bạn sẽ tăng thêm ~ 20% tốc độ.
Dyorgio

15

Hãy thử cách này:

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

Xử lý mảng (nếu tôi hiểu đúng về bạn):

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

Như polygenelubricants đã đề cập, String.format()là câu trả lời đúng so với Integer.toHexString()(vì nó liên quan đến các số âm theo đúng cách).


2
Điều này sẽ ký mở rộng, ví dụ thử -1.
đa gen

byte bv = 10; Chuỗi hexString = Integer.toHexString (bv); Điều này dường như hoạt động tốt .. Tôi có thể áp dụng nó riêng lẻ trên mọi phần tử của mảng .. Mã khác (Xử lý mảng) mang lại giá trị quá lớn. Điều gì có thể beaason cho điều đó?
Vivek

@Vivek, đó là vì trong trường hợp bvnó trả về một ký tự thập lục phân . Trong khi phần còn lại của mã trả về một chuỗi các ký tự thập lục phân . Tôi đã thay đổi mã với đồng hồ đo để bạn có thể hiểu nó ngay bây giờ.
0x2D9A3

@Bar: bạn vẫn có thể sử dụng Integer.toHexStringnếu bạn che dấu bytevới 0xFFđể mở rộng dấu hiệu lùi lại.
đa gen

Câu 1
Vivek

13

Giải pháp tốt nhất là lớp lót xấu này:

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

như đã đề cập ở đây


4
DatatypeConverter không còn có sẵn trong Java 9. Điều nguy hiểm là mã sử dụng nó sẽ biên dịch theo Java 1.8 trở về trước (Java 9 với các cài đặt nguồn thành trước đó), nhưng có một ngoại lệ thời gian chạy trong Java 9.
Stephen M -on strike-

Buồn khi bạn nói nó không có trong jdk9. new BigInteger(byteArray).toString(16)là con đường để đi Là những vấn đề hoàn hảo với điều đó ??
nguyện

Có thể không phải là vấn đề hoàn hảo, nhưng nó sẽ bỏ lỡ các số 0 đứng đầu (vì chúng vô nghĩa với một số như BigInteger)
Friso

Có vẻ như nó vẫn còn trong tài liệu java 9 , vì vậy có vẻ như vẫn ổn khi sử dụng nó từ những gì tôi có thể nói
Brad park

Tôi nghĩ như đã giải thích ở đây, nó vẫn ổn để sử dụng cho java9, nhưng sẽ bị xóa trong các bản phát hành java trong tương lai. bạn vẫn sẽ có thể sử dụng nó với mô-đun jaxb độc lập 'mới' kể từ phiên bản 2.3.0 .
lynx

11

Nếu bạn muốn một biểu diễn hex có chiều rộng không đổi, tức là 0Athay vì A, để bạn có thể khôi phục các byte một cách rõ ràng, hãy thử format():

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

11

Một cách ngắn gọn và đơn giản để chuyển đổi byte[]thành chuỗi hex bằng cách sử dụng 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

Làm thế nào nó hoạt động?

Lớp lớp hệ thống tích hợp java.math.BigInteger( java.math.BigInteger ) tương thích với dữ liệu nhị phân và hex:

  • Nó có một BigInteger(signum=1, byte[])hàm tạo để tạo một số nguyên lớn bằng cách byte[](đặt tham số đầu tiên signum= 1để xử lý chính xác các byte âm)
  • Sử dụng BigInteger.toString(16)để chuyển đổi số nguyên lớn thành chuỗi hex
  • Để phân tích cú pháp sử dụng số hex new BigInteger("ffa74b", 16)- không xử lý chính xác số 0 đứng đầu

Nếu bạn có thể muốn có số 0 đứng đầu trong kết quả hex, hãy kiểm tra kích thước của nó và thêm số 0 còn thiếu nếu cần:

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

Ghi chú

Sử dụng new BigInteger(1, bytes), thay vì new BigInteger(bytes), bởi vì Java " bị phá vỡ bởi thiết kế " và bytekiểu dữ liệu không chứa byte nhưng được ký các số nguyên nhỏ [-128 ... 127]. Nếu byte đầu tiên là âm, thì BigIntegergiả sử bạn truyền một số nguyên lớn âm. Chỉ cần vượt qua 1như tham số đầu tiên ( signum=1).

Việc chuyển đổi trở lại từ hex thànhbyte[] khó khăn: đôi khi một số 0 đứng đầu nhập vào đầu ra được sản xuất và nó cần được làm sạch như sau:

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

Lưu ý cuối cùng là nếu byte[]có một số 0 đầu, chúng sẽ bị mất.


1
Nếu byte hàng đầu có giá trị thập phân nhỏ hơn 16, bạn cũng sẽ nhận được một chuỗi có số ký tự hex lẻ.
Alex Jorgenson

8

Nếu bạn hài lòng khi sử dụng một thư viện bên ngoài, org.apache.commons.codec.binary.Hexlớp có một encodeHexphương thức lấy a byte[]và trả về a char[]. Phương pháp này NHIỀU nhanh hơn tùy chọn định dạng và gói gọn các chi tiết của chuyển đổi. Cũng đi kèm với một decodeHexphương pháp cho việc chuyển đổi ngược lại.


4
Một cách thậm chí còn dễ dàng hơn là sử dụng các hàm dựng sẵn javax.xml.bind.DatatypeConverter / parseHexBinary và printHexBinary. Xem stackoverflow.com/questions/9655181/ Mạnh
Alan Thompson

2
+1 cho tùy chọn này. Hex cũng có một phương thức encodeHexString, lấy một byte [] và trả về một Chuỗi.
Mingjiang Shi

đừng quên rằng javaxkhông gian tên không phải lúc nào cũng có sẵn.
Mene

7

Bạn có thể sử dụng phương pháp từ thư viện Bouncy Castle Carrier :

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

Gói Bouncy Castle Crypto là một triển khai Java của các thuật toán mã hóa. Bình này chứa nhà cung cấp JCE và API nhẹ cho API mã hóa Bouncy Castle cho JDK 1.5 đến JDK 1.8.

Phụ thuộc Maven:

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

hoặc từ Codec Apache Commons :

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

Gói Codec Apache Commons chứa bộ mã hóa và giải mã đơn giản cho các định dạng khác nhau như Base64 và Hexadecimal. Ngoài các bộ mã hóa và giải mã được sử dụng rộng rãi này, gói codec còn duy trì một bộ sưu tập các tiện ích mã hóa ngữ âm.

Phụ thuộc Maven:

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

Đúng, đây là giải pháp tốt nhất , nhưng yêu cầu một thư viện bên ngoài: Apache Commons Codec ( mvnreposective.com/artifact/commons-codec/commons-codec/1.11 ) hoặc Nhà cung cấp BouncyCastle ( mvnreposeective.com/artifact/org.bouncycastle/c jdk15on / 1.60 )
Svetlin Nakov

5

Đây là mã mà tôi đã tìm thấy để chạy nhanh nhất cho đến nay. Tôi đã chạy nó trên mảng 109015 byte có độ dài 32, trong 23ms. Tôi đã chạy nó trên máy ảo nên có lẽ nó sẽ chạy nhanh hơn trên kim loại trần.

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

Sau đó, bạn có thể làm

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

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

Không hoạt động: BigInteger(byteArrayOf(-1, 2, 3, 4, 5)).toString(16)trả về"-fdfcfbfb"
Martin Vysny

Đó là một kết quả đúng. Bạn làm việc với các byte '-1', '2' ... '5'. Đó là byte không có visualiztion ( unicode.org ) nếu bạn có ý định làm việc với chữ '-1', '2' ... '5' bạn nên làm việc với các giá trị chuỗi.
Wender

Đó là kết quả sai. Giá trị byte Java của -1 trên thực tế là 0xFF (nó giống như (int) 255) vì các byte Java đã được ký, do đó, kết quả sẽ là FF02030405. Nếu bạn thử giải pháp @Jerinaw ở trên, bạn sẽ thấy nó sẽ in đúng đầu ra. Cũng xem giải pháp của Svetlin Nakov dưới đây.
Martin Vysny

2

Đây là một chức năng đơn giản để chuyển đổi byte thành Hexadecimal

   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

Những người khác đã bao gồm các trường hợp chung. Nhưng nếu bạn có một mảng byte có dạng đã biết, ví dụ địa chỉ MAC, thì bạn có thể:

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

Tạo (và phá hủy) một loạt các Stringtrường hợp không phải là một cách tốt nếu hiệu suất là một vấn đề.

Vui lòng bỏ qua các câu lệnh kiểm tra đối số dài (trùng lặp if). Đó là cho (mục đích khác) giáo dục.

Dự án maven đầy đủ: http://jinahya.googlecode.com/svn/trunk/com.googlecode.jinahya/hex-codec/

Mã hóa ...

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

Giải mã ...

/**
 * 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

Đây là một cách rất nhanh. Không có libaries bên ngoài cần thiết.

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

Tôi không thể hiểu chính xác ý của bạn là gì bởi Chuỗi byte, nhưng đây là một số chuyển đổi từ byte sang Chuỗi và ngược lại, tất nhiên có nhiều hơn nữa trên các tài liệu chính thức

Integer intValue = 149;

Giá trị byte tương ứng là:

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

lấy lại giá trị nguyên từ biến Byte:

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

Từ Byte và Integer đến hex String:
Đây là cách tôi làm:

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

Chuyển đổi một mảng byte thành chuỗi hex:
Theo tôi biết, không có chức năng đơn giản nào để chuyển đổi tất cả các phần tử bên trong một mảng của một số Objectthành các phần tử khác Object, vì vậy bạn phải tự làm điều đó. Bạn có thể sử dụng các chức năng sau:

Từ byte [] đến String:

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

Và từ chuỗi hex sang byte []:

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

Đã quá muộn nhưng tôi hy vọng điều này có thể giúp một số người khác;)


1

Có phương pháp nhanh của bạn:

    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
xấu, nhưng có lẽ rất hiệu quả. :-)
Rich S

1

Cũng giống như một số câu trả lời khác, tôi khuyên bạn nên sử dụng String.format()BigInteger. Nhưng để diễn giải mảng byte là biểu diễn nhị phân lớn cuối thay vì biểu diễn nhị phân bổ sung của hai (với việc sử dụng dấu hiệu và không đầy đủ của phạm vi giá trị hex có thể), hãy sử dụng BigInteger (int Signum, byte [] độ lớn) , chứ không phải BigInteger (byte [] val ) .

Ví dụ, đối với một mảng byte có độ dài 8, hãy sử dụng:

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

Ưu điểm:

  • số không hàng đầu
  • không có dấu hiệu
  • chỉ các chức năng tích hợp
  • chỉ một dòng mã

Bất lợi:

  • có thể có nhiều cách hiệu quả hơn để làm điều đó

Thí dụ:

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

Ví dụ đầu ra:

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.