IT

Java에서 선행 0을 유지하면서 바이트 배열을 16 진수 문자열로 변환하는 방법은 무엇입니까?

lottoking 2020. 6. 3. 08:13
반응형

Java에서 선행 0을 유지하면서 바이트 배열을 16 진수 문자열로 변환하는 방법은 무엇입니까? [복제]


이 질문에는 이미 답변이 있습니다.

md5 해시를 만들기위한 예제 Java 코드로 작업하고 있습니다. 한 부분은 결과를 바이트에서 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은 분명히 선행 0을 제거하기 때문에 작동하지 않습니다. 그렇다면 바이트 배열에서 16 진수 문자열로 이동하여 선행 0을 유지하는 가장 간단한 방법은 무엇입니까?


간단한 접근 방법은 몇 자릿수가 출력되는지 확인 Integer.toHexString()하고 필요한 경우 각 바이트에 선행 0을 추가하는 것입니다. 이 같은:

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

Apache Commons Codec 에서 Hex.encodeHexString확인하십시오 .

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

String hex = Hex.encodeHexString(bytes);

아래 중 하나를 사용할 수 있습니다. 나는 이것을 선행 0 바이트와 초기 음수 바이트로 테스트했다.

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

16 진수 소문자를 원하면 "x"문자열 형식으로 사용 하십시오.


JAXB (Java Architecture for XML Binding)javax.xml.bind.DatatypeConverter.printHexBinary() 의 일부인 이 메소드 는 a 를 16 진 문자열 로 변환하는 편리한 방법이었습니다 . 클래스에는 다른 유용한 데이터 조작 방법도 많이 포함되었습니다.byte[]DatatypeConverter

Java 8 및 이전 버전에서 JAXB는 Java 표준 라이브러리의 일부였습니다. 그것은 한 사용되지 않는 자바 9 및 제거 자바 (11)와 함께 자신의 라이브러리로 모든 Java EE 패키지를 이동하기위한 노력의 일환으로. 긴 이야기 입니다. 이제는 javax.xml.bind존재하지 않으며을 포함하는 JAXB를 사용 DatatypeConverter하려면 Maven에서 JAXB APIJAXB 런타임 을 설치해야합니다 .

사용법 예 :

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

결과는 다음과 같습니다.

000086003D

나는 스티브의 제출물을 좋아했지만 몇 가지 변수를 사용하지 않고 프로세스에서 여러 줄을 저장할 수있었습니다.

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 블랙 박스 변환에 의존하는 대신) 정확히 무엇을하고 있는지 쉽게 알 수 있으며 선행 0과 같은 코너 케이스에 대해 걱정할 필요가 없다는 것입니다. 이 루틴은 모든 4 비트 니블을 가져와 16 진 문자로 바꿉니다. 그리고 테이블 조회를 사용하고 있기 때문에 아마도 빠릅니다. v / 16 및 v % 16을 비트 단위 시프트 및 AND로 바꾸면 더 빠를 수 있지만 지금 테스트하기에는 너무 게으르다.


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

나는 똑같은 것을 찾고 있었는데 ... 여기 좋은 아이디어가 있지만 몇 가지 마이크로 벤치 마크를 실행했습니다. 나는 다음이 가장 빠르다는 것을 알았습니다 (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와 본질적으로 동일하므로 선행 0을 제거 할 수 있습니다. 여전히 다음과 같이 수정하면 여전히 가장 빠릅니다.

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

static String toHex(byte[] digest) {
    StringBuilder sb = new StringBuilder();
    for (byte b : digest) {
        sb.append(String.format("%1$02X", b));
    }

    return sb.toString();
}

String result = String.format("%0" + messageDigest.length + "s", hexString.toString())

그것은 당신이 이미 가지고있는 것을 감안할 때 가장 짧은 해결책입니다. 바이트 배열을 숫자 값으로 String.format변환 할 수 있으면 동시에 16 진 문자열로 변환 할 수 있습니다.


구아바 는 그것을 매우 간단하게 만듭니다

BaseEncoding.base16().encode( bytes );

Apache Commons를 사용할 수없는 경우 좋은 대안입니다. 또한 다음과 같은 출력 제어 기능이 있습니다.

byte[] bytes = new byte[] { 0xa, 0xb, 0xc, 0xd, 0xe, 0xf };
BaseEncoding.base16().lowerCase().withSeparator( ":", 2 ).encode( bytes );
// "0a:0b:0c:0d:0e:0f"

해시와 같이 고정 길이에 다음과 같은 것을 사용합니다.

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

0마스크의는 패딩을하지 ...


이 솔루션은 약간 오래된 학교이므로 메모리 효율적이어야합니다.

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

다른 옵션

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

선행 0을 유지하기 위해 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));
}

죄송합니다. Ayman이 제안한 것보다 나빠 보입니다. 죄송합니다.


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

concat이 나타나고 추가 기능이 실제로 느려질 수 있습니다. 다음은 이전 게시물보다 훨씬 빨랐습니다. 출력을 빌드 할 때 char 배열로 변경하는 것이 속도를 높이는 핵심 요소입니다. Brandon DuRette가 제안한 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); }

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

편집 : 나는 테스트했고 이것으로 후행 0이 잘리는 것을 알았습니다. 그러나 이것은 처음에만 발생할 수 있으므로 예상 길이와 비교할 수 있습니다.


You can get it writing less without external libraries:

String hex = (new HexBinaryAdapter()).marshal(md5.digest(YOUR_STRING.getBytes()))

This solution requires no bit-shifting or -masking, lookup tables, or external libraries, and is about as short as I can get:

byte[] digest = new byte[16];       

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

fmt.toString()

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

IMHO all the solutions above that provide snippets to remove the leading zeroes are wrong.

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

According to this snippet, 8 bits are taken from the byte array in an iteration, converted into an integer (since Integer.toHexString function takes int as argument) and then that integer is converted to the corresponding hash value. So, for example if you have 00000001 00000001 in binary, according to the code, the hexString variable would have 0x11 as the hex value whereas correct value should be 0x0101. Thus, while calculating MD5 we may get hashes of length <32 bytes(because of missing zeroes) which may not satisfy the cryptographically unique properties that MD5 hash does.

The solution to the problem is replacing the above code snippet by the following snippet:

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

This will give two-char long string for a byte.

public String toString(byte b){
    final char[] Hex = new String("0123456789ABCDEF").toCharArray();
    return  "0x"+ Hex[(b & 0xF0) >> 4]+ Hex[(b & 0x0F)];
}

And how can you convert back again from ascii to byte array ?

i followed following code to convert to ascii given by Jemenake.

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

my variant

    StringBuilder builder = new StringBuilder();
    for (byte b : bytes)
    {
        builder.append(Character.forDigit(b/16, 16));
        builder.append(Character.forDigit(b % 16, 16));
    }
    System.out.println(builder.toString());

it works for me.


Is that a faulty solution? (android java)

    // Create MD5 Hash
    MessageDigest digest = java.security.MessageDigest.getInstance("MD5");
    digest.update(s.getBytes());
    byte[] md5sum = digest.digest();
    BigInteger bigInt = new BigInteger(1, md5sum);
    String stringMD5 = bigInt.toString(16);
    // Fill to 32 chars
    stringMD5 = String.format("%32s", stringMD5).replace(' ', '0');
    return stringMD5;

So basically it replaces spaces with 0.


I'm surprised that no one came up with the following solution:

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

Or you can do this:

byte[] digest = algorithm.digest();
StringBuilder byteContet = new StringBuilder();
for(byte b: digest){
 byteContent = String.format("%02x",b);
 byteContent.append(byteContent);
}

Its Short, simple and basically just a format change.


This is also equivalent but more concise using Apache util HexBin where the code reduces to

HexBin.encode(messageDigest).toLowerCase();

참고URL : https://stackoverflow.com/questions/332079/in-java-how-do-i-convert-a-byte-array-to-a-string-of-hex-digits-while-keeping-l

반응형