Check whether a string is parsable into Long without try-catch?

JavaStringParsingTry Catch

Java Problem Overview


Long.parseLong("string") throws an error if string is not parsable into long. Is there a way to validate the string faster than using try-catch? Thanks

Java Solutions


Solution 1 - Java

You can create rather complex regular expression but it isn't worth that. Using exceptions here is absolutely normal.

It's natural exceptional situation: you assume that there is an integer in the string but indeed there is something else. Exception should be thrown and handled properly.

If you look inside parseLong code, you'll see that there are many different verifications and operations. If you want to do all that stuff before parsing it'll decrease the performance (if we are talking about parsing millions of numbers because otherwise it doesn't matter). So, the only thing you can do if you really need to improve performance by avoiding exceptions is: copy parseLong implementation to your own function and return NaN instead of throwing exceptions in all correspondent cases.

Solution 2 - Java

From commons-lang StringUtils:

public static boolean isNumeric(String str) {
    if (str == null) {
        return false;
    }
    int sz = str.length();
    for (int i = 0; i < sz; i++) {
        if (Character.isDigit(str.charAt(i)) == false) {
            return false;
        }
    }
    return true;
}

Solution 3 - Java

You could do something like

if(s.matches("\\d*")){
}

Using regular expression - to check if String s is full of digits. But what do you stand to gain? another if condition?

Solution 4 - Java

org.apache.commons.lang3.math.NumberUtils.isParsable(yourString) will determine if the string can be parsed by one of: Integer.parseInt(String), Long.parseLong(String), Float.parseFloat(String) or Double.parseDouble(String)

Since you are interested in Longs you could have a condition that checks for isParsable and doesn't contain a decimal

if (NumberUtils.isParsable(yourString) && !StringUtils.contains(yourString,".")){ ...

Solution 5 - Java

This is a valid question because there are times when you need to infer what type of data is being represented in a string. For example, you may need to import a large CSV into a database and represent the data types accurately. In such cases, calling Long.parseLong and catching an exception can be too slow.

The following code only handles ASCII decimal:

public class LongParser {
    // Since tryParseLong represents the value as negative during processing, we
    // counter-intuitively want to keep the sign if the result is negative and
    // negate it if it is positive.
    private static final int MULTIPLIER_FOR_NEGATIVE_RESULT = 1;
    private static final int MULTIPLIER_FOR_POSITIVE_RESULT = -1;

    private static final int FIRST_CHARACTER_POSITION = 0;
    private static final int SECOND_CHARACTER_POSITION = 1;
    private static final char NEGATIVE_SIGN_CHARACTER = '-';
    private static final char POSITIVE_SIGN_CHARACTER = '+';
    private static final int DIGIT_MAX_VALUE = 9;
    private static final int DIGIT_MIN_VALUE = 0;
    private static final char ZERO_CHARACTER = '0';
    private static final int RADIX = 10;

    /**
     * Parses a string representation of a long significantly faster than
     * <code>Long.ParseLong</code>, and avoids the noteworthy overhead of
     * throwing an exception on failure. Based on the parseInt code from
     * http://nadeausoftware.com/articles/2009/08/java_tip_how_parse_integers_quickly
     *
     * @param stringToParse
     *            The string to try to parse as a <code>long</code>.
     *
     * @return the boxed <code>long</code> value if the string was a valid
     *         representation of a long; otherwise <code>null</code>.
     */
    public static Long tryParseLong(final String stringToParse) {
        if (stringToParse == null || stringToParse.isEmpty()) {
            return null;
        }

        final int inputStringLength = stringToParse.length();
        long value = 0;

        /*
         * The absolute value of Long.MIN_VALUE is greater than the absolute
         * value of Long.MAX_VALUE, so during processing we'll use a negative
         * value, then we'll multiply it by signMultiplier before returning it.
         * This allows us to avoid a conditional add/subtract inside the loop.
         */

        int signMultiplier = MULTIPLIER_FOR_POSITIVE_RESULT;

        // Get the first character.
        char firstCharacter = stringToParse.charAt(FIRST_CHARACTER_POSITION);

        if (firstCharacter == NEGATIVE_SIGN_CHARACTER) {
            // The first character is a negative sign.
            if (inputStringLength == 1) {
                // There are no digits.
                // The string is not a valid representation of a long value.
                return null;
            }

            signMultiplier = MULTIPLIER_FOR_NEGATIVE_RESULT;
        } else if (firstCharacter == POSITIVE_SIGN_CHARACTER) {
            // The first character is a positive sign.
            if (inputStringLength == 1) {
                // There are no digits.
                // The string is not a valid representation of a long value.
                return null;
            }
        } else {
            // Store the (negative) digit (although we aren't sure yet if it's
            // actually a digit).
            value = -(firstCharacter - ZERO_CHARACTER);
            if (value > DIGIT_MIN_VALUE || value < -DIGIT_MAX_VALUE) {
                // The first character is not a digit (or a negative sign).
                // The string is not a valid representation of a long value.
                return null;
            }
        }

        // Establish the "maximum" value (actually minimum since we're working
        // with negatives).
        final long rangeLimit = (signMultiplier == MULTIPLIER_FOR_POSITIVE_RESULT)
            ? -Long.MAX_VALUE
            : Long.MIN_VALUE;

        // Capture the maximum value that we can multiply by the radix without
        // overflowing.
        final long maxLongNegatedPriorToMultiplyingByRadix = rangeLimit / RADIX;

        for (int currentCharacterPosition = SECOND_CHARACTER_POSITION;
            currentCharacterPosition < inputStringLength;
            currentCharacterPosition++) {
            // Get the current digit (although we aren't sure yet if it's
            // actually a digit).
            long digit = stringToParse.charAt(currentCharacterPosition)
                    - ZERO_CHARACTER;

            if (digit < DIGIT_MIN_VALUE || digit > DIGIT_MAX_VALUE) {
                // The current character is not a digit.
                // The string is not a valid representation of a long value.
                return null;
            }

            if (value < maxLongNegatedPriorToMultiplyingByRadix) {
                // The value will be out of range if we multiply by the radix.
                // The string is not a valid representation of a long value.
                return null;
            }

            // Multiply by the radix to slide all the previously parsed digits.
            value *= RADIX;

            if (value < (rangeLimit + digit)) {
                // The value would be out of range if we "added" the current
                // digit.
                return null;
            }

            // "Add" the digit to the value.
            value -= digit;
        }

        // Return the value (adjusting the sign if needed).
        return value * signMultiplier;
    }
}

Solution 6 - Java

You can use java.util.Scanner

Scanner sc = new Scanner(s);
if (sc.hasNextLong()) {
   long num = sc.nextLong();
}

This does range checking etc, too. Of course it will say that "99 bottles of beer" hasNextLong(), so if you want to make sure that it only has a long you'd have to do extra checks.

Solution 7 - Java

This case is common for forms and programs where you have the input field and are not sure if the string is a valid number. So using try/catch with your java function is the best thing to do if you understand how try/catch works compared to trying to write the function yourself. In order to setup the try catch block in .NET virtual machine, there is zero instructions of overhead, and it is probably the same in Java. If there are instructions used at the try keyword then these will be minimal, and the bulk of the instructions will be used at the catch part and that only happens in the rare case when the number is not valid.

So while it "seems" like you can write a faster function yourself, you would have to optimize it better than the Java compiler in order to beat the try/catch mechanism you already use, and the benefit of a more optimized function is going to be very minimal since number parsing is quite generic.

If you run timing tests with your compiler and the java catch mechanism you already described, you will probably not notice any above marginal slowdown, and by marginal I mean it should be almost nothing.

Get the java language specification to understand the exceptions more and you will see that using such a technique in this case is perfectly acceptable since it wraps a fairly large and complex function. Adding on those few extra instructions in the CPU for the try part is not going to be such a big deal.

Solution 8 - Java

I think that's the only way of checking if a String is a valid long value. but you can implement yourself a method to do that, having in mind the biggest long value.

Solution 9 - Java

There are much faster ways to parse a long than Long.parseLong. If you want to see an example of a method that is not optimized then you should look at parseLong :)

Do you really need to take into account "digits" that are non-ASCII?

Do you really need to make several methods calls passing around a radix even tough you're probably parsing base 10?

:)

Using a regexp is not the way to go: it's harder to determine if you're number is too big for a long: how do you use a regexp to determine that 9223372036854775807 can be parsed to a long but that 9223372036854775907 cannot?

That said, the answer to a really fast long parsing method is a state machine and that no matter if you want to test if it's parseable or to parse it. Simply, it's not a generic state machine accepting complex regexp but a hardcoded one.

I can both write you a method that parses a long and another one that determines if a long can be parsed that totally outperforms Long.parseLong().

Now what do you want? A state testing method? In that case a state testing method may not be desirable if you want to avoid computing twice the long.

Simply wrap your call in a try/catch.

And if you really want something faster than the default Long.parseLong, write one that is tailored to your problem: base 10 if you're base 10, not checking digits outside ASCII (because you're probably not interested in Japanese's itchi-ni-yon-go etc.).

Solution 10 - Java

Hope this helps with the positive values. I used this method once for validating database primary keys.

private static final int MAX_LONG_STR_LEN = Long.toString(Long.MAX_VALUE).length();

public static boolean validId(final CharSequence id)
{
    //avoid null
    if (id == null)
    {
        return false;
    }

    int len = id.length();

    //avoid empty or oversize
    if (len < 1 || len > MAX_LONG_STR_LEN)
    {
        return false;
    }

    long result = 0;
    // ASCII '0' at position 48
    int digit = id.charAt(0) - 48;

    //first char cannot be '0' in my "id" case
    if (digit < 1 || digit > 9)
    {
        return false;
    }
    else
    {
        result += digit;
    }

    //start from 1, we already did the 0.
    for (int i = 1; i < len; i++)
    {
        // ASCII '0' at position 48
        digit = id.charAt(i) - 48;

        //only numbers
        if (digit < 0 || digit > 9)
        {
            return false;
        }

        result *= 10;
        result += digit;

        //if we hit 0x7fffffffffffffff
        // we are at 0x8000000000000000 + digit - 1
        // so negative
        if (result < 0)
        {
            //overflow
            return false;
        }
    }

    return true;
}

Solution 11 - Java

Try to use this regular expression:

^(-9223372036854775808|0)$|^((-?)((?!0)\d{1,18}|[1-8]\d{18}|9[0-1]\d{17}|92[0-1]\d{16}|922[0-2]\d{15}|9223[0-2]\d{14}|92233[0-6]\d{13}|922337[0-1]\d{12}|92233720[0-2]\d{10}|922337203[0-5]\d{9}|9223372036[0-7]\d{8}|92233720368[0-4]\d{7}|922337203685[0-3]\d{6}|9223372036854[0-6]\d{5}|92233720368547[0-6]\d{4}|922337203685477[0-4]\d{3}|9223372036854775[0-7]\d{2}|922337203685477580[0-7]))$

It checks all possible numbers for Long. But as you know in Java Long can contain additional symbols like +, L, _ and etc. And this regexp doesn't validate these values. But if this regexp is not enough for you, you can add additional restrictions for it.

Solution 12 - Java

Guava Longs.tryParse("string") returns null instead of throwing an exception if parsing fails. But this method is marked as Beta right now.

Solution 13 - Java

You could try using a regular expression to check the form of the string before trying to parse it?

Solution 14 - Java

A simple implementation to validate an integer that fits in a long would be:

    public static boolean isValidLong(String str) {
        if( str==null ) return false;
        int len = str.length();
        if (str.charAt(0) == '+') {
            return str.matches("\\+\\d+") && (len < 20 || len == 20 && str.compareTo("+9223372036854775807") <= 0);
        } else if (str.charAt(0) == '-') {
            return str.matches("-\\d+") && (len < 20 || len == 20 && str.compareTo("-9223372036854775808") <= 0);
        } else {
            return str.matches("\\d+") && (len < 19 || len == 19 && str.compareTo("9223372036854775807") <= 0);
        }
    }

It doesn't handle octal, 0x prefix or so but that is seldom a requirement.

For speed, the ".match" expressions are easy to code in a loop.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSergView Question on Stackoverflow
Solution 1 - JavaRomanView Answer on Stackoverflow
Solution 2 - JavalexicoreView Answer on Stackoverflow
Solution 3 - Javaring bearerView Answer on Stackoverflow
Solution 4 - JavaAndrew NormanView Answer on Stackoverflow
Solution 5 - JavaWoodyView Answer on Stackoverflow
Solution 6 - JavapolygenelubricantsView Answer on Stackoverflow
Solution 7 - JavaBob HolmesView Answer on Stackoverflow
Solution 8 - Javacd1View Answer on Stackoverflow
Solution 9 - JavaSyntaxT3rr0rView Answer on Stackoverflow
Solution 10 - JavaHannesView Answer on Stackoverflow
Solution 11 - JavaAliaksei MychkoView Answer on Stackoverflow
Solution 12 - JavaAnton YurievView Answer on Stackoverflow
Solution 13 - JavaMoonshieldView Answer on Stackoverflow
Solution 14 - JavaFlorian FView Answer on Stackoverflow