169

私は、md5 ハッシュを作成するためのサンプル Java コードを使用しています。1 つの部分は、結果をバイトから 16 進数の文字列に変換します。

byte messageDigest[] = algorithm.digest();     
StringBuffer hexString = new StringBuffer();
for (int i=0;i<messageDigest.length;i++) {
    hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
    }

ただし、 toHexString は明らかに先頭のゼロを削除するため、うまく機能しません。では、バイト配列から先行ゼロを保持する 16 進文字列に移行する最も簡単な方法は何ですか?

4

28 に答える 28

138

Apache Commons Codecの Hex.encodeHexStringを確認してください。

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

String hex = Hex.encodeHexString(bytes);
于 2008-12-01T22:12:58.117 に答える
116

以下のものを使用できます。これを先頭のゼロバイトと最初の負のバイトでもテストしました

public static String toHex(byte[] bytes) {
    BigInteger bi = new BigInteger(1, bytes);
    return String.format("%0" + (bytes.length << 1) + "X", bi);
}

小文字の 16 進数が必要な場合"x"は、文字列形式で使用します。

于 2009-06-03T10:09:07.433 に答える
112

簡単な方法は、出力される桁数を確認しInteger.toHexString()、必要に応じて各バイトに先行ゼロを追加することです。このようなもの:

public static String toHexString(byte[] bytes) {
    StringBuilder hexString = new StringBuilder();

    for (int i = 0; i < bytes.length; i++) {
        String hex = Integer.toHexString(0xFF & bytes[i]);
        if (hex.length() == 1) {
            hexString.append('0');
        }
        hexString.append(hex);
    }

    return hexString.toString();
}
于 2008-12-01T20:32:19.893 に答える
40

Java Architecture for XML Binding (JAXB)javax.xml.bind.DatatypeConverter.printHexBinary()の一部であるメソッドは、 a を 16 進文字列に変換する便利な方法でした。このクラスには、他にも多くの便利なデータ操作メソッドが含まれていました。byte[]DatatypeConverter

Java 8 以前では、JAXB は Java 標準ライブラリの一部でした。すべての Java EE パッケージを独自のライブラリに移動する取り組みの一環として、Java 9 で廃止れ、Java 11で削除されました。長い話です。javax.xml.bindが含まれている JAXB を使用する場合は、Maven からJAXB APIJAXB ランタイムDatatypeConverterをインストールする必要があります。

使用例:

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

結果は次のとおりです。

000086003D
于 2013-01-27T21:50:58.610 に答える
35

私は Steve の提出物が気に入りましたが、彼はいくつかの変数を使用せずに、プロセスでいくつかの行を節約できたはずです。

public static String toHexString(byte[] bytes) {
    char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
    char[] hexChars = new char[bytes.length * 2];
    int v;
    for ( int j = 0; j < bytes.length; j++ ) {
        v = bytes[j] & 0xFF;
        hexChars[j*2] = hexArray[v/16];
        hexChars[j*2 + 1] = hexArray[v%16];
    }
    return new String(hexChars);
}

これについて私が気に入っているのは、(魔法の BigInteger ブラック ボックス変換に頼るのではなく) 何をしているかを正確に確認するのが簡単で、先行ゼロなどのコーナー ケースについて心配する必要がないことです。このルーチンは、すべての 4 ビット ニブルを取得し、それを 16 進文字に変換します。また、テーブル ルックアップを使用しているため、おそらく高速です。v/16 と v%16 をビットごとのシフトと AND に置き換えるとおそらく高速になる可能性がありますが、今はテストするのが面倒です。

于 2010-02-04T06:03:37.043 に答える
22

Integer.toHexString が少し遅いことがわかりました。多くのバイトを変換する場合は、"00".."FF" を含む文字列の配列を作成し、整数をインデックスとして使用することを検討してください。いえ

hexString.append(hexArray[0xFF & messageDigest[i]]);

これはより速く、正しい長さを保証します。文字列の配列が必要なだけです:

String[] hexArray = {
"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"};
于 2009-06-15T17:15:58.290 に答える
12

私は同じことを探していました...ここでいくつかの良いアイデアがありますが、いくつかのマイクロベンチマークを実行しました。私は次のことが最も速いことを発見しました(上記のAymanから変更され、約2倍速く、このすぐ上のSteveよりも約50%高速です):

public static String hash(String text, String algorithm)
        throws NoSuchAlgorithmException {
    byte[] hash = MessageDigest.getInstance(algorithm).digest(text.getBytes());
    return new BigInteger(1, hash).toString(16);
}

編集:おっと-これは本質的にkgiannakakisのものと同じであるため、先行ゼロを取り除く可能性があることを見逃しました。それでも、これを次のように変更すると、それでも最速です。

public static String hash(String text, String algorithm)
        throws NoSuchAlgorithmException {
    byte[] hash = MessageDigest.getInstance(algorithm).digest(text.getBytes());
    BigInteger bi = new BigInteger(1, hash);
    String result = bi.toString(16);
    if (result.length() % 2 != 0) {
        return "0" + result;
    }
    return result;
}
于 2009-06-26T03:56:03.730 に答える
11
static String toHex(byte[] digest) {
    StringBuilder sb = new StringBuilder();
    for (byte b : digest) {
        sb.append(String.format("%1$02X", b));
    }

    return sb.toString();
}
于 2010-04-14T14:55:04.773 に答える
6
String result = String.format("%0" + messageDigest.length + "s", hexString.toString())

すでに持っているものを考えると、それが最短の解決策です。バイト配列を数値に変換できればString.format、同時に16進文字列に変換できます。

于 2008-12-01T20:33:15.930 に答える
5

別のオプション

public static String toHexString(byte[]bytes) {
    StringBuilder sb = new StringBuilder(bytes.length*2);
    for(byte b: bytes)
      sb.append(Integer.toHexString(b+0x800).substring(1));
    return sb.toString();
}
于 2009-06-03T21:04:01.900 に答える
5

このソリューションは少し古い学校であり、メモリ効率が良いはずです。

public static String toHexString(byte bytes[]) {
    if (bytes == null) {
        return null;
    }

    StringBuffer sb = new StringBuffer();
    for (int iter = 0; iter < bytes.length; iter++) {
        byte high = (byte) ( (bytes[iter] & 0xf0) >> 4);
        byte low =  (byte)   (bytes[iter] & 0x0f);
        sb.append(nibble2char(high));
        sb.append(nibble2char(low));
    }

    return sb.toString();
}

private static char nibble2char(byte b) {
    byte nibble = (byte) (b & 0x0f);
    if (nibble < 10) {
        return (char) ('0' + nibble);
    }
    return (char) ('a' + nibble - 10);
}
于 2008-12-02T15:21:12.580 に答える
4

先頭のゼロを維持するために、Paul が提案したものの小さなバリエーション (例: md5 ハッシュ) を次に示します。

public static String MD5hash(String text) throws NoSuchAlgorithmException {
    byte[] hash = MessageDigest.getInstance("MD5").digest(text.getBytes());
    return String.format("%032x",new BigInteger(1, hash));
}

おっと、これはアイマンが提案したものよりも貧弱に見えます。申し訳ありません

于 2010-07-13T10:02:03.340 に答える
4
static String toHex(byte[] digest) {
    String digits = "0123456789abcdef";
    StringBuilder sb = new StringBuilder(digest.length * 2);
    for (byte b : digest) {
        int bi = b & 0xff;
        sb.append(digits.charAt(bi >> 4));
        sb.append(digits.charAt(bi & 0xf));
    }
    return sb.toString();
}
于 2010-10-08T08:57:37.570 に答える
3

concatおよびappend関数は非常に遅くなる可能性があるようです。以下は私にとってはるかに速かった(私の前の投稿よりも)。出力を作成する際にchar配列に変更することが、出力を高速化するための重要な要素でした。BrandonDuRetteによって提案されたHex.encodeHexとは比較していません。

public static String toHexString(byte[] bytes) {
    char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
    char[] hexChars = new char[10000000];
    int c = 0;
    int v;
    for ( j = 0; j < bytes.length; j++ ) {
        v = bytes[j] & 0xFF;
        hexChars[c] = hexArray[v/16];
        c++;
        hexChars[c] = hexArray[v%16];
        c++;
    }
    return new String(hexChars, 0, c); }
于 2009-06-29T18:26:13.513 に答える
2

これは私がMD5ハッシュに使用しているものです:

public static String getMD5(String filename)
        throws NoSuchAlgorithmException, IOException {
    MessageDigest messageDigest = 
        java.security.MessageDigest.getInstance("MD5");

    InputStream in = new FileInputStream(filename);

    byte [] buffer = new byte[8192];
    int len = in.read(buffer, 0, buffer.length);

    while (len > 0) {
        messageDigest.update(buffer, 0, len);
        len = in.read(buffer, 0, buffer.length);
    }
    in.close();

    return new BigInteger(1, messageDigest.digest()).toString(16);
}

EDIT:私はテストしましたが、これでも末尾のゼロがカットされていることに気付きました。ただし、これは最初にのみ発生する可能性があるため、予想される長さと比較して、それに応じてパディングすることができます。

于 2008-12-01T20:30:05.547 に答える
2

このソリューションは、ビットシフトやマスキング、ルックアップ テーブル、または外部ライブラリを必要とせず、可能な限り短いものです。

byte[] digest = new byte[16];       

Formatter fmt = new Formatter();    
for (byte b : digest) { 
  fmt.format("%02X", b);    
}

fmt.toString()
于 2012-09-21T00:16:59.877 に答える
2

外部ライブラリなしで書き込みを減らすことができます:

String hex = (new HexBinaryAdapter()).marshal(md5.digest(YOUR_STRING.getBytes()))
于 2012-09-20T14:07:33.367 に答える
1
byte messageDigest[] = algorithm.digest();
StringBuffer hexString = new StringBuffer();
for (int i = 0; i < messageDigest.length; i++) {
    String hexByte = Integer.toHexString(0xFF & messageDigest[i]);
    int numDigits = 2 - hexByte.length();
    while (numDigits-- > 0) {
        hexString.append('0');
    }
    hexString.append(hexByte);
}
于 2008-12-01T20:41:14.773 に答える
0

IMHO 先行ゼロを削除するためのスニペットを提供する上記のすべてのソリューションは間違っています。

byte messageDigest[] = algorithm.digest();
for (int i = 0; i < messageDigest.length; i++) {
    hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
}    

このスニペットによると、8 ビットが反復でバイト配列から取得され、整数に変換され (Integer.toHexString 関数は int を引数として受け取るため)、その整数が対応するハッシュ値に変換されます。したがって、たとえば、バイナリで 00000001 00000001 がある場合、コードによると、hexString 変数は 16 進値として 0x11 を持ちますが、正しい値は 0x0101 である必要があります。したがって、MD5 の計算中に長さが 32 バイト未満のハッシュが得られる場合があり (ゼロがないため)、MD5 ハッシュが行う暗号学的に一意のプロパティを満たさない可能性があります。

この問題の解決策は、上記のコード スニペットを次のスニペットに置き換えることです。

byte messageDigest[] = algorithm.digest();
for (int i = 0; i < messageDigest.length; i++) {
    int temp=0xFF & messageDigest[i];
    String s=Integer.toHexString(temp);
    if(temp<=0x0F){
        s="0"+s;
    }
    hexString.append(s);
}
于 2012-01-27T10:42:10.467 に答える
0

そして、どうすれば ascii からバイト配列に戻すことができますか?

次のコードに従って、Jemenake から提供された ascii に変換しました。

public static String toHexString(byte[] bytes) {
    char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
    char[] hexChars = new char[bytes.length * 2];
    int v;
    for ( int j = 0; j < bytes.length; j++ ) {
        v = bytes[j] & 0xFF;
        hexChars[j*2] = hexArray[v/16];
        hexChars[j*2 + 1] = hexArray[v%16];
    }
    return new String(hexChars);
}
于 2013-06-12T10:53:12.007 に答える
0

次の解決策を誰も思いつかなかったことに驚いています。

StringWriter sw = new StringWriter();
com.sun.corba.se.impl.orbutil.HexOutputStream hex = new com.sun.corba.se.impl.orbutil.HexOutputStream(sw);
hex.write(byteArray);
System.out.println(sw.toString());
于 2016-02-17T20:34:31.767 に答える
0

これにより、1 バイトに対して 2 文字の長い文字列が得られます。

public String toString(byte b){
    final char[] Hex = new String("0123456789ABCDEF").toCharArray();
    return  "0x"+ Hex[(b & 0xF0) >> 4]+ Hex[(b & 0x0F)];
}
于 2013-01-30T22:24:33.897 に答える
-1

これも同等ですが、Apache util HexBinを使用するとより簡潔になります。

HexBin.encode(messageDigest).toLowerCase();
于 2012-08-27T14:20:36.257 に答える