[java] Java 코드 바이트를 16 진수로 변환하려면

바이트 배열이 있습니다. 해당 배열의 각 바이트 문자열을 해당하는 16 진수 값으로 변환하고 싶습니다.

Java에서 바이트 배열을 16 진수로 변환하는 함수가 있습니까?



답변

    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'-결과가 0으로 채워집니다.
      • 2
      • 변환 'X'-결과는 16 진수 정수 (대문자)로 형식이 지정됩니다

질문의 텍스트를 보면 이것이 요청 된 것일 수도 있습니다.

    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"

byteJava로 서명 된 8 비트 는 32 비트로 확장됩니다 int. 이 부호 확장을 효과적으로 실행 취소하기 위해 bytewith를 마스크 할 수 있습니다 0xFF.

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

사용의 또 다른 문제 toHexString는 0으로 채워지지 않는다는 것입니다.

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

두 가지 요소를 결합하면 String.format솔루션이 더 선호됩니다.

참고 문헌


답변

기존 답변 중 왜 그들의 접근 방식이 효과가 있는지 설명하지 않기 때문에 게시하고 있습니다.이 문제에 정말 중요하다고 생각합니다. 경우에 따라 제안 된 솔루션이 불필요하게 복잡하고 미묘 해 보일 수 있습니다. 설명하기 위해 상당히 간단한 접근 방식을 제공하지만 그 이유를 설명하는 데 도움이되도록 좀 더 자세히 설명 하겠습니다.

우선, 우리는 무엇을하려고합니까? 바이트 값 (또는 바이트 배열)을 ASCII로 16 진수 값을 나타내는 문자열로 변환하려고합니다. 따라서 첫 번째 단계는 Java에서 바이트가 무엇인지 정확히 찾는 것입니다.

바이트 데이터 형식은 8 비트 부호있는 2의 보수 정수 입니다. 최소값은 -128이고 최대 값은 127입니다 (포함). 바이트 데이터 유형은 메모리 절약이 실제로 중요한 큰 배열로 메모리를 저장하는 데 유용 할 수 있습니다. 또한 그들의 한계가 코드를 명확하게하는 데 도움이되는 int 대신 사용될 수도 있습니다. 변수의 범위가 제한되어 있다는 사실은 문서 형태로 작용할 수 있습니다.

이것은 무엇을 의미 하는가? 몇 가지 : 첫째, 가장 중요한 것은 8 비트로 작업하고 있음을 의미 합니다. 예를 들어 숫자 2를 0000 0010으로 쓸 수 있습니다. 그러나 2의 보수이므로 다음과 같이 음수 2를 씁니다 : 1111 1110. 또한 16 진수로 변환하는 것은 매우 간단합니다. 즉, 각 4 비트 세그먼트를 16 진수로 직접 변환하기 만하면됩니다. 이 체계에서 음수를 이해하려면 먼저 2의 보수를 이해해야합니다. 2의 보수를 아직 이해하지 못하면 여기에서 훌륭한 설명을 읽을 수 있습니다. http://www.cs.cornell.edu/~tomf/notes/cps104/twoscomp.html


일반적으로 2의 보수를 16 진수로 변환

숫자가 2의 보수에 들어가면 16 진수로 변환하는 것은 간단합니다. 일반적으로 바이너리에서 16 진수로 변환하는 것은 매우 간단하며 다음 두 예에서 볼 수 있듯이 2의 보수에서 16 진수로 직접 이동할 수 있습니다.

예 1 : 2를 16 진수로 변환하십시오.

1) 먼저 2의 보수에서 2를 이진수로 변환하십시오.

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

2) 이제 바이너리를 16 진수로 변환하십시오.

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

therefore 2 = 0000 0010 = 0x02. 

예 2 : -2 (2의 보수)를 16 진으로 변환하십시오.

1) 먼저 2의 보수에서 -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) 이제 16 진수로 변환하십시오.

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

therefore: -2 = 1111 1110 = 0xFE.


자바에서 이것을하기

이제 개념을 다루었으므로 간단한 마스킹 및 시프트를 통해 원하는 것을 달성 할 수 있습니다. 이해해야 할 핵심은 변환하려는 바이트가 이미 2의 보수에 있다는 것입니다. 이 변환을 직접 수행하지 마십시오. 나는 이것이이 문제에 대한 혼란의 주요 지점이라고 생각합니다. 다음 바이트 배열을 예로 들어 보겠습니다.

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

위의 16 진수로 수동으로 변환했지만 Java로 어떻게 할 수 있습니까? 방법은 다음과 같습니다.

1 단계 : 계산을 보유 할 StringBuffer를 만듭니다.

StringBuffer buffer = new StringBuffer();

2 단계 : 상위 비트를 분리하여 16 진수로 변환 한 후 버퍼에 추가

이진수 1111 1110이 주어지면 우선 상위 비트를 4 씩 시프트 한 다음 나머지 숫자를 0으로 만들어 분리 할 수 ​​있습니다. 논리적으로 이것은 간단하지만 Java (및 많은 언어)의 구현 세부 사항은 부호 확장으로 인해 주름을 유발합니다. 기본적으로 바이트 값을 이동하면 Java는 먼저 값을 정수로 변환 한 다음 부호 확장을 수행합니다. 따라서 1111 1110 >> 4는 0000 1111이 될 것으로 예상되지만 실제로 Java에서는 2의 보수 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 함수는 전달한 숫자를 16 진수 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();
}

~보다 50 배 빠릅니다 String.format. 테스트하고 싶다면 :

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가 언급했듯이 (정확한 방법으로 음수를 처리 String.format()하기 Integer.toHexString()때문에) 정답 입니다.


답변

최선의 해결책은 다음과 같은 불량한 단일 라이너입니다.

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

여기에 언급 된 바와 같이


답변

바이트를 명확하게 복구 할 수 있도록 일정한 너비의 16 진수 표현을 원한다면 (예 : 0A대신) 다음을 A시도하십시오 format().

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


답변

다음 byte[]을 사용하여 16 진 문자열 로 변환 하는 간단하고 간단한 방법 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

작동 원리

내장 시스템 클래스 java.math.BigInteger클래스 ( java.math.BigInteger )는 2 진 및 16 진 데이터와 호환됩니다.

  • 그것에 BigInteger(signum=1, byte[])의해 큰 정수를 생성 하는 생성자 가 있습니다 byte[](첫 번째 매개 변수 설정 signum=1 음수 바이트를 올바르게 처리하기 위해)
  • BigInteger.toString(16)큰 정수를 16 진 으로 변환하는 데 사용 문자열
  • 16 진수 사용을 구문 분석하려면 new BigInteger("ffa74b", 16)선행 0을 올바르게 처리하지 않습니다.

16 진 결과에서 선행 0 을 원할 경우 크기를 확인하고 필요한 경우 누락 된 0을 추가하십시오.

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

노트

Java가 ” 설계에 의해 손상됨 “이고 데이터 유형이 바이트를 보유하지 않고 부호있는 작은 정수 [-128 … 127] 이므로 new BigInteger(1, bytes)대신 대신을 사용하십시오 . 첫 번째 바이트가 음수이면 음수 큰 정수를 전달한다고 가정합니다. 첫 번째 매개 변수로 전달하십시오 (new BigInteger(bytes)byteBigInteger1signum=1 ) .

16 진수에서 16 진수로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[]몇 개의 선행 0이 있으면 손실됩니다.