programing

Java에서는 선행 0을 유지하면서 바이트 배열을 16진수 문자열로 변환하려면 어떻게 해야 합니까?

prostudy 2022. 6. 27. 20:49
반응형

Java에서는 선행 0을 유지하면서 바이트 배열을 16진수 문자열로 변환하려면 어떻게 해야 합니까?

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

Hex.encode를 확인합니다.Apache Commons 코덱의 HexString.

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"스트링 스트링

은 몇 입니다.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();
}

메서드는 Java Architecture for XML Binding(JAXB)의 일부이며, 이 메서드를 사용하면, EXB 를 간단하게 변환할 수 있습니다.byte[]16진수 스트링으로 변환합니다.이 클래스에는 다른 유용한 데이터 조작 방법도 다수 포함되어 있었다.

Java 8 이전 버전에서는 JAXB는 Java 표준 라이브러리의 일부였습니다.모든 Java EE 패키지를 자체 라이브러리로 이동하기 위한 노력의 일환으로 Java 9에서 더 이상 사용되지 않고 Java 11에서 제거되었습니다.얘기하자면 길어.지금이다,javax.xml.bind하지 않으며, JAXB가 되어 있는 JAXB를,DatatypeConverterMaven에서 JAXB APIJAXB Runtime을 설치해야 합니다.

사용 예:

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 블랙박스 변환에 의존하지 않고 정확히 무엇을 하고 있는지 알 수 있고, 선행 제로와 같은 코너 케이스에 대해 걱정할 필요가 없다는 점입니다.이 루틴은 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);
}

편집: 오류 - 기본적으로 kjiannakakis와 같기 때문에 선행 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();
}

고정 길이에는 다음과 같은 해시를 사용합니다.

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

0★★★★★★★★★★★★★★★★...

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

이미 가지고 계신 걸 감안하면 그게 가장 빠른 해결책입니다.할 수 는, 「」를 참조해 주세요.String.format는 동시에 16진수 문자열로 변환할 수 있습니다.

Guava는 또한 매우 심플합니다.

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"

이 솔루션은 약간 구식이므로 메모리 효율이 높을 것입니다.

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

아이먼이 제안한 것보다 더 형편없어 보여서 미안해

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와 append 함수가 매우 느릴 수 있습니다.아래는 (이전 게시물보다) 훨씬 더 빨랐습니다.출력을 빌드할 때 char 배열로 변경하는 것이 출력을 고속화하는 주요 요인이었습니다.Hex.encode와 비교한 적이 없습니다.Brandon DuRette가 제안한 Hex.

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

EDIT: 테스트를 해봤는데, 이것과 함께 후행 0도 잘려나간다는 것을 알게 되었습니다.그러나 이것은 처음에만 발생할 수 있기 때문에 예상 길이와 패드를 적절히 비교할 수 있습니다.

외부 라이브러리를 사용하지 않고 쓰기 작업을 줄일 수 있습니다.

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

이 솔루션에는 비트 쉬프나 마스킹, 룩업 테이블 또는 외부 라이브러리가 필요하지 않으며, 가능한 한 짧은 시간이 필요합니다.

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는 선행 0을 제거하기 위한 스니펫을 제공하는 위의 모든 솔루션이 잘못되었습니다.

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바이트 미만인 해시(0이 누락되었기 때문에)를 얻을 수 있습니다.이 해시는 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);
}

그러면 1바이트에 대해 2char 길이의 문자열이 생성됩니다.

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

그리고 어떻게 다시 asciii에서 byte 배열로 변환할 수 있습니까?

나는 다음 코드를 따라 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);
}

나의 변종

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

나한테는 효과가 있어

그게 잘못된 해결책인가요?(자바 포함)

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

기본적으로 공간을 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());

또는 다음과 같이 할 수 있습니다.

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

짧고 단순하며 기본적으로 형식 변경에 불과합니다.

이 또한 동등하지만 Apache util HexBin을 사용하면 코드가 다음과 같이 감소합니다.

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

반응형