Verify if String is hexadecimal

79,731

Solution 1

There's an overloaded Long.parseLong that accepts a second parameter, specifying the radix:

Long.parseLong(cadena,16);

As an alternative, you could iterate over the characters in the string and call Character.digit(c,16) on them (if any of them return -1 it's not a valid hexadecimal digit). This is especially useful if the string is too large to fit in a long (as pointed out in the comments, that would cause an exception if the first method is used). Example:

private static boolean isNumeric(String cadena) {
    if ( cadena.length() == 0 || 
         (cadena.charAt(0) != '-' && Character.digit(cadena.charAt(0), 16) == -1))
        return false;
    if ( cadena.length() == 1 && cadena.charAt(0) == '-' )
        return false;

    for ( int i = 1 ; i < cadena.length() ; i++ )
        if ( Character.digit(cadena.charAt(i), 16) == -1 )
            return false;
    return true;
}

BTW, I'd suggest separating the concerns of "testing for a valid number" and "displaying a message to the user", that's why I simply returned false in the example above instead of notifying the user first.

Finally, you could simply use a regular expression:

cadena.matches("-?[0-9a-fA-F]+");

Solution 2

Horrible abuse of exceptions. Don't ever do this! (It's not me, it's Josh Bloch's Effective Java). Anyway, I suggest

private static final Pattern HEXADECIMAL_PATTERN = Pattern.compile("\\p{XDigit}+");

private boolean isHexadecimal(String input) {
    final Matcher matcher = HEXADECIMAL_PATTERN.matcher(input);
    return matcher.matches();
}

Solution 3

Used this in my own code to check if string is a MAC address

boolean isHex = mac_addr.matches("^[0-9a-fA-F]+$");

My beef with the other answers provided in this thread is that if the String's length is large, it would throw an exception as well. Therefore, not very useful if you are testing if a MAC address consist of valid hexadecimals.

Don't be terrified of using regex!

Solution 4

Here some code for different options and execution time results (JDK 11):

execution time isHex1: 3670 ms
execution time isHex2: 3294 ms
execution time isHex3: 3946 ms
execution time regex: 31288 ms

Test code:

public class HexPerformanceTest {
    @Test
    public void testPerformance() {
        int count = 100000000;
        char[] chars = {
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f',
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
        };
        String regexString = new String(chars);

        Predicate<String> isHex1Test = testString -> {
            boolean isHex = true;
            for (char c: testString.toCharArray()) {
                if (!isHex1(c)) {
                    isHex = false;
                    break;
                }
            }
            return isHex;
        };

        Predicate<String> isHex2Test = testString -> {
            boolean isHex = true;
            for (char c: testString.toCharArray()) {
                if (!isHex2(c)) {
                    isHex = false;
                    break;
                }
            }
            return isHex;
        };

        Predicate<String> isHex3Test = testString -> {
            boolean isHex = true;
            for (char c: testString.toCharArray()) {
                if (!isHex3(c)) {
                    isHex = false;
                    break;
                }
            }
            return isHex;
        };

        Pattern pattern = Pattern.compile("^[0-9a-fA-F]+$");
        Predicate<String> regexTest = testString -> {
            Matcher matcher = pattern.matcher(regexString);
            return matcher.matches();
        };

        System.out.println("execution time isHex1: " + milliseconds(regexString, isHex1Test, count) + " ms");
        System.out.println("execution time isHex2: " + milliseconds(regexString, isHex2Test, count) + " ms");
        System.out.println("execution time isHex3: " + milliseconds(regexString, isHex3Test, count) + " ms");
        System.out.println("execution time regex: " + milliseconds(regexString, regexTest, count) + " ms");
    }

    private long milliseconds(String testString, Predicate<String> hexTest, int count) {
        long start = System.currentTimeMillis();
        for (int i = 0; i < count; i++) {
            hexTest.test(testString);
        }
        return System.currentTimeMillis() - start;
    }

    private boolean isHex1(char c) {
        return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
    }

    private boolean isHex2(char c) {
        switch (c) {
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
            case 'a':
            case 'b':
            case 'c':
            case 'd':
            case 'e':
            case 'f':
            case 'A':
            case 'B':
            case 'C':
            case 'D':
            case 'E':
            case 'F':
                return true;
            default:
                return false;
        }
    }

    private boolean isHex3(char c) {
        return (Character.digit(c, 16) != -1);
    }
}

Solution 5

Long.parseLong has a second form that takes a radix as its second argument.

private static boolean isHexNumber (String cadena) {
  try {
    Long.parseLong(cadena, 16);
    return true;
  }
  catch (NumberFormatException ex) {
    // Error handling code...
    return false;
  }
}
Share:
79,731
Admin
Author by

Admin

Updated on December 11, 2021

Comments

  • Admin
    Admin over 2 years

    I have a String like "09a" and I need a method to confirm if the text is hexadecimal. The code I've posted does something similar, it verifies that a string is a decimal number. I want to do the same, but for hexadecimal.

        private static boolean isNumeric(String cadena) {
        try {
            Long.parseLong(cadena);
            return true;
        } catch (NumberFormatException nfe) {
            JOptionPane.showMessageDialog(null,"Uno de los números, excede su capacidad.");
            return false;
        }
    }
    
  • mgibsonbr
    mgibsonbr almost 12 years
    As an alternative, you could iterate over the characters in the string and call Character.forDigit(c,16) on them (if any of them return null - '\0' - it's not a valid hexadecimal digit). Or use a regular expression (though it would be more complicated and possibly perform worse).
  • Oliver
    Oliver almost 12 years
    If you want to check if the input is in hexadecimal format, you shouldn't trim it at the beginning. Also, your loop could fail faster if you just return false on the first occurrence of a non-hex character (there's probably some method to check for containment so you could do: if (!hexDigits.Contains(symbol)) return false;).
  • samthebest
    samthebest almost 10 years
    Your misquoting Effective Java, one should avoid exceptions to control logic flow. Provided incorrect hex strings are indeed an exceptional occurrence, then there is no reason not to use parseLong and catch the exception. Josh's book gives an example where the exception will always be thrown to control a loop, this is not really like that. Furthermore Josh's concerns are also to do with efficiency and readability, but you propose REGEX, which is both slower and less readable.
  • samthebest
    samthebest almost 10 years
    Now (from my benchmarking) using your regex is a bit slower than using the parseLong assuming that the vast majority of the strings are actually correct. I actually agree that using exceptions might not be perfect, and there are better solutions out there, my main objection to your answer is that you suggest something that is worse than using exceptions.
  • Kevin Wheeler
    Kevin Wheeler over 9 years
    The Character.forDigit(..) approach you specified is invalid. Character.forDigit(..) takes in an int and converts it to a char, not the other way around.
  • mgibsonbr
    mgibsonbr over 9 years
    @KevinWheeler Thanks for the correction! The right method indeed is Character.digit(char, int), assuming no characters outside BMP. In this case, a non-digit character would result in -1 instead of null.
  • Czyzby
    Czyzby over 9 years
    This is not a very efficient way to check if a String is hexadecimal and the ternary operator in the return statement is redundant.
  • Czyzby
    Czyzby over 9 years
    This will compile the regex each time it's checked, it's generally better to use a Pattern.
  • Czyzby
    Czyzby over 9 years
    This will compile the regex each time it's checked, it's generally better to use a Pattern.
  • Czyzby
    Czyzby over 9 years
    This will also throw an exception if the String is too long, which might not be desired (especially if the String is used to create a BigInteger, for example).
  • mgibsonbr
    mgibsonbr over 9 years
    @JustACluelessNewbie Thanks for pointing that out! I moved my first comment (that does not have this problem) to the answer's body.
  • Czyzby
    Czyzby over 9 years
    While we're at it, your isNumeric method returns false for Strings with "-" before the number, which might be valid negative numbers. Before the iteration, I'd put a check if the first char is a '-' and if yes, the iteration should start from the second index. It should also return false if '-' is the only character in the String.
  • Junior Mayhé
    Junior Mayhé over 8 years
    Remark: Long.parseLong("f7e511c46d1b8a03", 16) won't work, which will fire NumberFormatException (invalid long). So iterate or regex could be better choices for bigger hexadecimals.
  • Yury
    Yury almost 4 years
    +1 for the only solution here that doesn't loop through the string characters, which I generally consider yucky from a purely aesthetic standpoint (even if it's more efficient) :)
  • Mark Stewart
    Mark Stewart almost 4 years
    Issue of compiling noted by @Czyzby fixed in answer by later edits. Enjoy!
  • Jurabek Azizkhujaev
    Jurabek Azizkhujaev over 3 years
    @samthebest google banned exceptions on their development on Java and C++, therefore in Golang also no exceptions )
  • samthebest
    samthebest over 3 years
    @JurabekAzizkhujaev I can agree with that, but it would be much better to ban Java and C++ altogether ;). I'm sure Go probably has really nice libraries to handle exceptions some other way. In functional programming we use validation Monads, which is awesome.
  • Gavriel
    Gavriel about 3 years
    what happens if I call isHexNumber("1234567890abcdef1234567890abcdef") ? Will get some other exception even though it is a hex number, just too long for "long"