簡體   English   中英

Java代碼將字節轉換為十六進制

[英]Java code To convert byte to Hexadecimal

我有一個字節數組。 我希望將該數組的每個字節字符串轉換為其相應的十六進制值。

Java 中是否有將字節數組轉換為十六進制的函數?

    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"

在 Java 中簽名的 8 位byte被符號擴展為 32 位int 為了有效地撤消此符號擴展,可以使用0xFF屏蔽byte

    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解決方案更受歡迎。

參考

我發帖是因為現有的答案都沒有解釋為什么他們的方法有效,我認為這對於這個問題非常重要。 在某些情況下,這會導致建議的解決方案顯得不必要地復雜和微妙。 為了說明,我將提供一個相當簡單的方法,但我將提供更多細節來幫助說明它為什么起作用。

首先,我們要做什么? 我們想將一個字節值(或一個字節數組)轉換為一個字符串,該字符串表示 ASCII 中的十六進制值。 所以第一步是找出Java中的字節到底是什么:

字節數據類型是一個8 位有符號二進制補碼整數 它的最小值為 -128,最大值為 127(含)。 字節數據類型可用於在大型數組中節省內存,其中內存節省實際上很重要。 它們也可以代替 int 使用它們的限制有助於澄清您的代碼; 變量范圍有限的事實可以作為一種文檔形式。

這是什么意思? 一些事情:首先,也是最重要的,這意味着我們正在使用8-bits 例如,我們可以將數字 2 寫為 0000 0010。但是,由於它是二進制補碼,因此我們將負數 2 寫成這樣:1111 1110。這也意味着轉換為十六進制非常簡單。 也就是說,您只需將每個 4 位段直接轉換為十六進制。 請注意,要理解此方案中的負數,您首先需要了解二進制補碼。 如果你還不了解二進制補碼,你可以在這里閱讀一個很好的解釋: http : //www.cs.cornell.edu/~tomf/notes/cps104/twoscomp.html


一般將二進制補碼轉換為十六進制

一旦數字處於二進制補碼中,將其轉換為十六進制非常簡單。 通常,從二進制轉換為十六進制非常簡單,正如您將在接下來的兩個示例中看到的,您可以直接從二進制補碼轉換為十六進制。

例子

示例 1:將 2 轉換為十六進制。

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(以二進制補碼形式)轉換為十六進制。

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)現在轉換為十六進制:

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. 

在 Java 中,我們可以一次性完成這一切:

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 循環並轉換整個數組:

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

希望這個解釋能讓你們中的那些想知道在互聯網上找到的許多例子中到底發生了什么的人更清楚。 希望我沒有犯任何嚴重的錯誤,但非常歡迎建議和更正!

我還發現這樣做的最快方法如下:

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

它比String.format快約 50 倍。 如果你想測試它:

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
        }

    }
}

編輯:剛剛發現了一些更快的東西,並且在一行上保持不變,但與 JRE 9不兼容使用風險自負

import javax.xml.bind.DatatypeConverter;

DatatypeConverter.printHexBinary(raw);

試試這個方法:

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 所提到的,與Integer.toHexString()相比, String.format()是正確的答案(因為它以正確的方式處理負數)。

使用BigIntegerbyte[]轉換為十六進制字符串的簡短方法:

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[]有幾個前導零,它們將丟失。

最好的解決方案是這個壞蛋單行:

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

正如這里提到的

如果您想要一個等寬的十六進制表示,即0A而不是A ,以便您可以明確地恢復字節,請嘗試format()

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

如果您樂於使用外部庫, org.apache.commons.codec.binary.Hex類有一個encodeHex方法,它接受一個byte[]並返回一個char[] 這種方法比格式選項快得多,並且封裝了轉換的細節。 還帶有用於相反轉換的decodeHex方法。

您可以使用Bouncy Castle Provider庫中的方法:

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

Bouncy Castle Crypto 包是加密算法的 Java 實現。 此 jar 包含 JCE 提供程序和用於 JDK 1.5 到 JDK 1.8 的 Bouncy Castle 加密 API 的輕量級 API。

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>

這是迄今為止我發現運行速度最快的代碼。 我在 23 毫秒內在長度為 32 的 109015 字節數組上運行它。 我在虛擬機上運行它,所以它可能會在裸機上運行得更快。

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) );
BigInteger n = new BigInteger(byteArray);
String hexa = n.toString(16));

這是一個將字節轉換為十六進制的簡單函數

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

其他人已經涵蓋了一般情況。 但是如果你有一個已知形式的字節數組,例如一個 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]); 

有你的快速方法:

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

就像其他一些答案一樣,我建議使用String.format()BigInteger 但是要將字節數組解釋為big-endian 二進制表示而不是二進制補碼表示(帶有符號和不完整使用可能的十六進制值范圍)使用BigInteger(int signum, byte[] mass) ,而不是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

如果性能是一個問題,創建(和銷毀)一堆String實例不是一個好方法。

請忽略那些冗長(重復)的參數檢查語句( if s)。 這是為了(另一個)教育目的。

完整的 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);
}

這是一個非常快速的方法。 不需要外部庫。

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

我無法弄清楚字節字符串到底是什么意思,但這里有一些從字節到字符串的轉換,反之亦然,當然官方文檔還有很多

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

從字節和整數到十六進制字符串:
這是我這樣做的方式:

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

為時已晚,但我希望這可以幫助其他人;)

如果您喜歡流,這里是格式和連接方法的單表達式版本:

String hex = IntStream.range(0, bytes.length)
                      .map(i -> bytes[i] & 0xff)
                      .mapToObj(b -> String.format("%02x", b))
                      .collect(Collectors.joining());

很遺憾沒有像Arrays::streamUnsignedBytes這樣的方法。

Java 17:引入java.util.HexFormat

Java 17 附帶了一個實用程序,可將字節數組和數字轉換為其十六進制對應物。 假設我們有一個“Hello World”的 MD5 摘要作為字節數組:

var md5 = MessageDigest.getInstance("md5");
md5.update("Hello world".getBytes(UTF_8));

var digest = md5.digest();

現在我們可以使用HexFormat.of().formatHex(byte[])方法將給定的byte[]轉換為其十六進制形式:

jshell> HexFormat.of().formatHex(digest)
$7 ==> "3e25960a79dbc69b674cd4ec67a72c62"

withUpperCase()方法返回前一個輸出的大寫版本:

jshell> HexFormat.of().withUpperCase().formatHex(digest)
$8 ==> "3E25960A79DBC69B674CD4EC67A72C62"

如果您使用 Tink,則有:

package com.google.crypto.tink.subtle;

public final class Hex {
  public static String encode(final byte[] bytes) { ... }
  public static byte[] decode(String hex) { ... }
}

所以這樣的事情應該有效:

import com.google.crypto.tink.subtle.Hex;

byte[] bytes = {-1, 0, 1, 2, 3 };
String enc = Hex.encode(bytes);
byte[] dec = Hex.decode(enc)

只需添加我的兩分錢,因為我看到許多使用字符數組和/或使用 StringBuilder 實例的答案並聲稱速度更快。

由於我使用 ASCII 表組織有不同的想法,代碼點 48-57 為 0-9,AF 為 65-70,af 為 97-102,我想測試哪個想法最快。

由於我已經有很多年沒有做過這樣的事情了,所以我對此進行了廣泛的拍攝。 我在不同大小的數組(1M、1K、10)中使用了 10 億個字節,所以每個數組有 1000 次 1M 字節,每個數組有 1M 次 1000 字節和 100M 次每個數組 10 個字節。

結果是 1-F 的字符數組獲勝。 使用 char 數組作為輸出而不是 StringBuilder 也可以勝任(更少的對象,沒有容量測試,沒有新數組,也沒有增長時的復制)。 此外,在使用 foreach (for(var b : bytes)) 循環時,您似乎會受到一點懲罰。

使用我的想法的版本大約是。 每個陣列 1M 字節慢 15%,每個陣列 1000 字節慢 21%,每個陣列 10 字節慢 18%。 StringBuilder 版本慢了 210%、380% 和 310%。

糟糕但並不出乎意料,因為在一級緩存中查找小數組勝過 if 和 add... 。 (一次緩存訪問 + 偏移量計算與一次如果,一次跳轉,一次添加 -> 不確定跳轉)。

我的版本:

public static String bytesToHex(byte [] bytes) {
    
    char [] result = new char [bytes.length * 2];
    
    for(int index = 0; index < bytes.length; index++) {
        int v = bytes[index]; 

        int upper = (v >>> 4) & 0xF;
        result[index * 2] = (char)(upper + (upper < 10 ? 48 : 65 - 10));
        
        int lower = v & 0xF;
        result[index * 2 + 1] = (char)(lower + (lower < 10 ? 48 : 65 - 10));
    }
    
    return new String(result);
}

PS:是的,我進行了多次運行,每次運行都取得了最佳運行效果,進行了熱身,還運行了 100 億個字符以確保相同的圖片......

Integer.toHexString((int)b);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM