Program Tip

문자열이 try-catch없이 Long으로 구문 분석 가능한지 확인하십시오.

programtip 2020. 12. 4. 20:19
반응형

문자열이 try-catch없이 Long으로 구문 분석 가능한지 확인하십시오.


Long.parseLong("string")문자열을 long으로 파싱 할 수없는 경우 오류가 발생합니다. 사용하는 것보다 빠르게 문자열의 유효성을 검사하는 방법이 try-catch있습니까? 감사


다소 복잡한 정규식을 만들 수는 있지만 그만한 가치는 없습니다. 여기서 예외를 사용하는 것은 절대적으로 정상입니다.

이것은 자연스러운 예외적 인 상황입니다. 문자열에 정수가 있다고 가정하지만 실제로는 다른 것이 있습니다. 예외가 발생하고 적절하게 처리되어야합니다.

parseLong코드 내부를 살펴보면 다양한 확인 및 작업이 있음을 알 수 있습니다. 파싱하기 전에 모든 작업을 수행하려면 성능이 저하됩니다 (그렇지 않으면 중요하지 않기 때문에 수백만 개의 숫자를 파싱하는 것에 대해 이야기하는 경우). 따라서 예외를 방지하여 성능을 향상 시켜야 하는 경우 수행 할 수있는 유일한 방법 parseLong구현을 자신의 함수에 복사 하고 모든 해당 사례에서 예외를 던지는 대신 NaN을 반환하는 것입니다.


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

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

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

정규식 사용-String s가 숫자로 가득 차 있는지 확인합니다. 그러나 당신은 무엇을 얻기 위해 서 있습니까? 또 다른 if 조건?


문자열로 표현되는 데이터 유형을 추론해야 할 때가 있기 때문에 이것은 유효한 질문입니다. 예를 들어 큰 CSV를 데이터베이스로 가져 와서 데이터 유형을 정확하게 나타내야 할 수 있습니다. 이러한 경우 Long.parseLong을 호출하고 예외를 포착하는 것이 너무 느릴 수 있습니다.

다음 코드는 ASCII 십진수 만 처리합니다.

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

당신이 사용할 수있는 java.util.Scanner

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

이것은 범위 검사 등도 수행합니다. 물론라고 표시 될 "99 bottles of beer" hasNextLong()것이므로 a 있는지 확인 long하려면 추가 검사를 수행해야합니다.


org.apache.commons.lang3.math.NumberUtils.isParsable (yourString)은 Integer.parseInt (String), Long.parseLong (String), Float.parseFloat (String) 또는 Double 중 하나로 문자열을 구문 분석 할 수 있는지 여부를 결정합니다. .parseDouble (문자열)

Long에 관심이 있으므로 isParsable을 확인하고 소수를 포함하지 않는 조건을 가질 수 있습니다.

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

이 경우는 입력 필드가 있고 문자열이 유효한 숫자인지 확실하지 않은 양식 및 프로그램에서 일반적입니다. 따라서 자바 함수와 함께 try / catch를 사용하는 것은 함수를 직접 작성하려는 것과 비교하여 try / catch가 어떻게 작동하는지 이해한다면 가장 좋은 방법입니다. .NET 가상 머신에서 try catch 블록을 설정하려면 오버 헤드에 대한 지침이 없으며 Java에서도 동일합니다. try 키워드에 사용 된 명령어가 있다면 이것들은 최소화 될 것이고, 대부분의 명령어는 catch 부분에서 사용될 것이고 이것은 숫자가 유효하지 않을 때 드문 경우에만 발생합니다.

따라서 더 빠른 함수를 직접 작성할 수있는 것처럼 "보이지만"이미 사용하고있는 try / catch 메커니즘을이기려면 Java 컴파일러보다 더 잘 최적화해야하며 더 최적화 된 함수의 이점은 다음과 같습니다. 숫자 구문 분석이 매우 일반적이기 때문에 최소화해야합니다.

이미 설명한 컴파일러와 자바 캐치 메커니즘을 사용하여 타이밍 테스트를 실행하면 위의 한계 속도 저하를 눈치 채지 못할 것입니다.

예외를 더 많이 이해하기 위해 자바 언어 사양을 가져 오면이 경우 이러한 기술을 사용하는 것이 상당히 크고 복잡한 함수를 래핑하기 때문에 완벽하게 허용된다는 것을 알 수 있습니다. try 부분을 위해 CPU에 몇 가지 추가 명령을 추가하는 것은 그렇게 큰 문제가 아닙니다.


이것이 String이 유효한 long 값인지 확인하는 유일한 방법이라고 생각합니다. 하지만 가장 큰 long 값을 염두에두고이를 수행하는 방법을 직접 구현할 수 있습니다.


Long.parseLong 보다 long구문 분석 하는 훨씬 빠른 방법이 있습니다 . 최적화 되지 않은 메서드의 예를 보려면 parseLong을 살펴 봐야합니다. :)

ASCII가 아닌 "숫자"를 정말로 고려해야합니까?

10 진법을 파싱하는 것이 힘들더라도 기수 주위를 전달 하는 여러 메서드 호출 을 정말로해야 합니까?

:)

정규식을 사용하는 것은 갈 길이 아닙니다 : 숫자가 오랫동안 너무 큰지 확인하는 것이 더 어렵습니다. 정규식을 사용하여 9223372036854775807을 long으로 구문 분석 할 수 있지만 9223372036854775907은 할 수 없다는 것을 어떻게 결정합니까?

즉, 정말 빠른 긴 구문 분석 방법에 대한 대답 상태 머신이며 구문 분석 가능한지 또는 구문 분석할지 여부에 관계없이 상태 머신입니다. 간단히 말해서 복잡한 regexp를 받아들이는 일반적인 상태 머신이 아니라 하드 코딩 된 것입니다.

나는 long을 파싱하는 메서드와 Long.parseLong () 을 완전히 능가하는 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.).


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

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.


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


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


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.

참고URL : https://stackoverflow.com/questions/2563608/check-whether-a-string-is-parsable-into-long-without-try-catch

반응형