2
\$\begingroup\$

Here is my attempt at converting hex strings to byte arrays and converting byte arrays to hex strings:

net.coderodde.util.ByteStringConverter

package net.coderodde.util;

import java.util.Scanner;

public class ByteStringConverter {

    /**
     * Converts the given byte array to its textual hex representation.
     * @param bytes the byte array to stringify.
     * @return the string representing the input byte array.
     */
    public static String convertByteArrayToHexString(byte[] bytes) {
        StringBuilder stringBuilder = new StringBuilder(2 * bytes.length);

        for (byte b : bytes) {
            stringBuilder.append(convertByteToHexString(b));
        }

        return stringBuilder.toString();
    }

    /**
     * Converts the given hex string to the byte array it represents.
     * @param hexString the hex string to convert.
     * @return the byte array represented by the input hex string.
     */
    public static byte[] convertHexStringToByteArray(String hexString) {
        byte[] byteArray = new byte[hexString.length() / 2];

        for (int i = 0; i < hexString.length(); i += 2) {
            byteArray[i / 2] = convertHexByteStringToByte(
                    hexString.substring(i, i + 2));
        }

        return byteArray;
    }

    /**
     * Converts the input character {@code c} to the nibble it represents. This
     * method assumes that {@code c} is numeric or within range 
     * {@code a, b, c, d, e, f}.
     * @param c the character to convert to a nibble.
     * @return the byte value of the textual representation of a nibble.
     */
    private static byte convertHexCharToNibble(char c) {
        c = Character.toLowerCase(c);

        switch (c) {
            case '0':
                return 0;
            case '1':
                return 1;
            case '2':
                return 2;
            case '3':
                return 3;
            case '4':
                return 4;
            case '5':
                return 5;
            case '6':
                return 6;
            case '7':
                return 7;
            case '8':
                return 8;
            case '9':
                return 9;
            case 'a':
                return 10;
            case 'b':
                return 11;
            case 'c':
                return 12;
            case 'd':
                return 13;
            case 'e':
                return 14;
            case 'f':
                return 15;

            default:
                throw new IllegalArgumentException("Not a hex digit: " + c);
        }
    }

    /**
     * Converts the input hex byte string to the byte it represents.
     * @param hexByteString the hex byte string to convert.
     * @return the byte value represented by {@code hexByteString}.
     */
    private static byte convertHexByteStringToByte(String hexByteString) {
        char lo = hexByteString.charAt(1);
        char hi = hexByteString.charAt(0);
        byte lob = convertHexCharToNibble(lo);
        byte hib = convertHexCharToNibble(hi);
        return (byte)((hib << 4) | lob);
    }

    /**
     * Converts the given byte to its textual hex representation.
     * @param b the byte to convert.
     * @return the textual representation of the byte {@code b}.
     */
    private static String convertByteToHexString(byte b) {
        byte lo = (byte)(b & (byte) 0xf);
        byte hi = (byte)((b >>> 4) & (byte) 0xf);
        StringBuilder stringBuilder = new StringBuilder(2);
        appendNibbleToStringBuilder(stringBuilder, hi);
        appendNibbleToStringBuilder(stringBuilder, lo);
        return stringBuilder.toString();
    }

    /**
     * Appends the textual representation of {@code nibble} to 
     * {@code stringBuilder}.
     * @param stringBuilder the target string builder.
     * @param nibble the nibble to append.
     */
    private static void appendNibbleToStringBuilder(StringBuilder stringBuilder, 
                                                    byte nibble) {
        switch (nibble) {
            case 0:
                stringBuilder.append('0');
                break;
            case 1:
                stringBuilder.append('1');
                break;
            case 2:
                stringBuilder.append('2');
                break;
            case 3:
                stringBuilder.append('3');
                break;
            case 4:
                stringBuilder.append('4');
                break;
            case 5:
                stringBuilder.append('5');
                break;
            case 6:
                stringBuilder.append('6');
                break;
            case 7:
                stringBuilder.append('7');
                break;
            case 8:
                stringBuilder.append('8');
                break;
            case 9:
                stringBuilder.append('9');
                break;
            case 10:
                stringBuilder.append('a');
                break;
            case 11:
                stringBuilder.append('b');
                break;
            case 12:
                stringBuilder.append('c');
                break;
            case 13:
                stringBuilder.append('d');
                break;
            case 14:
                stringBuilder.append('e');
                break;
            case 15:
                stringBuilder.append('f');
                break;
        }
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        while (true) {
            String hexString = scanner.next();
            byte[] hexBytes = convertHexStringToByteArray(hexString);
            String newHexString = convertByteArrayToHexString(hexBytes);
            System.out.println(newHexString);
        }
    }
}

As always, any critique is much appreciated!

\$\endgroup\$
3
\$\begingroup\$

You implemented convertByteArrayToHexString really inefficiently because you allocate a new string for each byte that is converted.

Also, there's no need for a StringBuilder since a character array suffices. The idiomatic code for converting a byte array to a hex string is:

public static String toHex(byte[] bytes) {
    char[] chars = new char[2 * bytes.length];
    for (int i = 0; i < bytes.length; i++) {
        chars[2 * i] = "0123456789abcdef".charAt((bytes[i] & 0xf0) >> 4);
        chars[2 * i + 1] = "0123456789abcdef".charAt(bytes[i] & 0x0f);
    }
    return new String(chars);
}

I wonder why you chose the inefficient variant over this straightforward code.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.