바이트 배열이 있습니다. 해당 배열의 각 바이트 문자열을 해당하는 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"
byte
Java로 서명 된 8 비트 는 32 비트로 확장됩니다 int
. 이 부호 확장을 효과적으로 실행 취소하기 위해 byte
with를 마스크 할 수 있습니다 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
솔루션이 더 선호됩니다.
참고 문헌
- JLS 4.2.1 정수 타입과 값
- 를 들어
byte
,에서-128
에127
, 포함
- 를 들어
- JLS 5.1.2 확장 원시 변환
답변
기존 답변 중 왜 그들의 접근 방식이 효과가 있는지 설명하지 않기 때문에 게시하고 있습니다.이 문제에 정말 중요하다고 생각합니다. 경우에 따라 제안 된 솔루션이 불필요하게 복잡하고 미묘 해 보일 수 있습니다. 설명하기 위해 상당히 간단한 접근 방식을 제공하지만 그 이유를 설명하는 데 도움이되도록 좀 더 자세히 설명 하겠습니다.
우선, 우리는 무엇을하려고합니까? 바이트 값 (또는 바이트 배열)을 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)
byte
BigInteger
1
signum=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이 있으면 손실됩니다.