[java] Java에서 바이트 배열을 16 진수 문자열로 변환하는 방법은 무엇입니까?

16 진수로 채워진 바이트 배열이 있고 인쇄 할 수없는 요소가 많기 때문에 쉬운 방법으로 인쇄하는 것은 무의미합니다. 내가 필요한 것은 다음과 같은 형태의 정확한 16 진수 코드입니다.3a5f771c



답변

토론에서 여기에 , 특히 대답이 내가 현재 사용하는 기능은 다음과 같습니다

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 바이트 천만 번)는 다른 대안보다 훨씬 빠르며 긴 배열의 절반에 달합니다. 내가 얻은 대답과 비교하여, 논의에서 제안한대로 비트 단위 연산으로 전환하면 긴 배열의 시간을 약 20 % 단축했습니다. (편집 : 대안보다 빠르면 토론에서 제공되는 대체 코드를 의미합니다. 성능은 매우 유사한 코드를 사용하는 Commons Codec와 같습니다.)

Java 9 컴팩트 문자열과 관련하여 2k20 버전 :

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


답변

아파치 코 몬즈 코덱 라이브러리는이 진수 작업의 바로 이런 종류의 작업을 수행하는 클래스.

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

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


답변

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 byteArrayToHex(byte[] a) {
   StringBuilder sb = new StringBuilder(a.length * 2);
   for(byte b: a)
      sb.append(String.format("%02x", b));
   return sb.toString();
}


답변

완전성을위한 구아바 솔루션 :

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

지금 hex"48656c6c6f20776f726c64"입니다.


답변

이 간단한 oneliner는 나를 위해 일합니다.
String result = new BigInteger(1, inputBytes).toString(16);
편집-이것을 사용하면 선행 0이 제거되지만 내 유스 케이스에서 일했습니다. 지적 해 주셔서 감사합니다 @Voicu


답변

다음은 단순 (한 줄짜리)에서 복잡한 (거대한 라이브러리)에 이르는 일반적인 옵션입니다. 성능에 관심이 있다면 아래의 마이크로 벤치 마크를 참조하십시오.

옵션 1 : 코드 스 니펫-단순

매우 간단한 해결책 중 하나는 BigInteger의 16 진 표현 을 사용하는 것입니다 .

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

참고이 손잡이 때문에 것을 없는 임의의 바이트 문자열 은 앞의 0을 생략합니다 -이 또는 당신이 (예를 들어 원하는 것을하지 않을 수 있습니다 000AE30AE33 바이트 입력). 이에 대해 매우 느린 또한 100 배 느린 다음 옵션에 비해.

옵션 2 : 코드 스 니펫-고급

대문자 / 소문자엔디안을 지원하는 모든 기능을 갖춘 복사 및 붙여 넣기 가능한 코드 스 니펫 입니다. 메모리 복잡성을 최소화하고 성능을 최대화하도록 최적화되었으며 모든 최신 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 인코더 / 디코더가 포함됩니다.

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

Github 에서 확인할 수 있습니다 : bytes-java .

옵션 4 : Apache Commons 코덱

물론 좋은 ‘ol commons 코덱 ‘이 있습니다. ( 앞서 경고 의견 ) 위에서 설명한 프로젝트를 진행하는 동안 나는 코드를 분석했고 매우 실망했다. 많은 중복 구성되지 않은 코드, 더 이상 사용되지 않는 이국적인 코덱은 널리 사용되는 코덱 (특히 Base64)의 구현이 매우 적고 느리게 구현 된 경우에만 유용합니다. 그러므로 나는 당신이 그것을 사용하거나 대안을 원한다면 정보에 근거한 결정을 내릴 것입니다. 어쨌든, 여전히 사용하려면 코드 스 니펫이 있습니다.

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

옵션 5 : 구글 구아바

더 자주 당신은 이미 구아바 를 의존으로 사용 하지 않습니다 . 그렇다면 다음을 사용하십시오.

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

옵션 6 : 스프링 보안

Spring Security 와 함께 Spring 프레임 워크 를 사용하는 경우 다음을 사용할 수 있습니다.

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

옵션 7 : 탄력성

보안 프레임 워크 Bouncy Castle 을 이미 사용중인 경우 해당 Hex유틸리티를 사용할 수 있습니다 .

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

실제로는 아님 옵션 8 : Java 9+ 호환성 또는 ‘JAXBs javax / xml / bind / DatatypeConverter를 사용하지 마십시오’

이전 Java (8 이하) 버전에서는 JAXB 용 Java 코드가 런타임 종속성으로 포함되었습니다. Java 9 및 Jigsaw는 모듈화 되기 때문에 코드가 명시 적 선언 없이는 모듈 외부의 다른 코드에 액세스 할 수 없습니다. 따라서 다음과 같은 예외가 발생하면주의하십시오.

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

Java 9 이상이 설치된 JVM에서 실행할 때 그렇다면 구현을 위의 대안 중 하나로 전환하십시오. 이 질문 도 참조하십시오 .


마이크로 벤치 마크

다음은 크기가 다른 간단한 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. 여기 에서 전체 벤치 마크를 참조 하십시오 .