Подтвердить что ты не робот

Проверьте, является ли строка синтаксической в ​​Long без try-catch?

Long.parseLong("string") выдает ошибку, если строка не разбирается в длине. Есть ли способ проверить строку быстрее, чем с помощью try-catch? Благодаря

4b9b3361

Ответ 1

Вы можете создать довольно сложное регулярное выражение, но этого не стоит. Исключение здесь абсолютно нормально.

Это естественная исключительная ситуация: вы предполагаете, что в строке есть целое число, но действительно есть что-то еще. Исключение должно быть выбрано и обработано должным образом.

Если вы посмотрите код parseLong, вы увидите, что существует множество различных проверок и операций. Если вы хотите сделать все это до разбора, это снизит производительность (если мы говорим о разборе миллионов чисел, потому что в противном случае это не имеет значения). Таким образом, единственное, что вы можете сделать, если вам действительно нужно улучшить производительность, избегая исключений: copy parseLong реализация в свою собственную функцию и вернуть NaN вместо того, чтобы бросать исключения во всех случаях корреляции.

Ответ 2

Из общедоступных строк 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;
}

Ответ 3

Вы можете сделать что-то вроде

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

Использование регулярного выражения - чтобы проверить, заполнена ли строка s цифрами. Но что вы выиграете? другое, если условие?

Ответ 4

Вы можете использовать java.util.Scanner

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

Это также проверка диапазона и т.д. Конечно, он скажет, что "99 bottles of beer" hasNextLong(), поэтому, если вы хотите, чтобы он только имел long, вам нужно будет выполнить дополнительные проверки.

Ответ 5

Это правильный вопрос, потому что есть моменты, когда вам нужно указать, какой тип данных будет представлен в строке. Например, вам может потребоваться импортировать большой 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;
    }
}

Ответ 6

org.apache.commons.lang3.math.NumberUtils.isParsable(yourString) определит, может ли строка быть проанализирована одним из следующих: Integer.parseInt(String), Long.parseLong(String), Float.parseFloat(String ) или Double.parseDouble(String)

Поскольку вас интересует Longs, у вас может быть условие, которое проверяет isParsable и не содержит десятичное число

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

Ответ 7

Этот случай является общим для форм и программ, в которых у вас есть поле ввода, и не уверены, является ли строка допустимым числом. Поэтому использование try/catch с помощью java-функции - это лучшее, что можно сделать, если вы понимаете, как работает try/catch по сравнению с попыткой написать функцию самостоятельно. Чтобы настроить блок catch try на виртуальной машине .NET, есть нулевые инструкции по накладным расходам, и, вероятно, это похоже на Java. Если в ключе try есть инструкции, они будут минимальными, и основная часть инструкций будет использоваться в части catch, и это произойдет только в редком случае, когда число недействительно.

Так что, пока он "кажется", как вы можете написать более быструю функцию самостоятельно, вам придется оптимизировать ее лучше, чем компилятор Java, чтобы превзойти механизм try/catch, который вы уже используете, и преимущество более оптимизированной функции будет очень минимальным, поскольку парсинг чисел является довольно общим.

Если вы запускаете тесты времени с вашим компилятором и механизм вылова java, о котором вы уже описали, вы, вероятно, не заметите какого-либо превышения предельного замедления, а маргинальным я имею в виду, что это должно быть почти ничего.

Получите спецификацию языка java, чтобы лучше понять исключения, и вы увидите, что использование такого метода в этом случае вполне приемлемо, поскольку оно обертывает довольно большую и сложную функцию. Добавление на эти несколько дополнительных инструкций в CPU для части try не будет таким большим делом.

Ответ 8

Я думаю, что единственный способ проверить, является ли String допустимым длинным значением. но вы можете реализовать себе способ сделать это, имея в виду самую большую ценность.

Ответ 9

Существует гораздо более быстрый способ проанализировать длинный, чем Long.parseLong. Если вы хотите увидеть пример метода, который не оптимизирован, вы должны посмотреть на parseLong:)

Вам действительно нужно учитывать "цифры", которые не являются ASCII?

Вам действительно нужно сделать несколько методов, вызывающих, проходящих вокруг радикса, даже жестко, вы, вероятно, разбираете базу 10?

:)

Использование регулярного выражения - это не путь: сложнее определить, является ли вы слишком длинным в течение долгого времени: как вы используете регулярное выражение для определения того, что 9223372036854775807 можно разобрать дольше, но 9223372036854775907 не может?

Тем не менее, ответ на очень быстрый метод синтаксического анализа - это конечный автомат и что независимо от того, хотите ли вы проверить, обрабатывается ли он или анализируется. Просто это не общий конечный автомат, принимающий сложное регулярное выражение, а жестко закодированное.

Я могу написать вам метод, который анализирует длинный и другой, который определяет, может ли длинный анализ быть полностью превосходен Long.parseLong().

Теперь, что вам нужно? Государственный метод тестирования? В этом случае метод тестирования состояния может быть нежелательным, если вы хотите избежать вычисления в два раза длиннее.

Просто заверните свой вызов в try/catch.

И если вы действительно хотите что-то быстрее, чем значение по умолчанию Long.parseLong, напишите, что соответствует вашей проблеме: base 10, если вы базовый 10, не проверяя цифры вне ASCII (потому что вы, вероятно, не интересуетесь японским itchi-ni-yon-go и т.д.).

Ответ 10

Надеюсь, что это поможет с положительными значениями. Я использовал этот метод один раз для проверки первичных ключей базы данных.

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

Ответ 11

Попробуйте использовать это регулярное выражение:

^(-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]))$

Он проверяет все возможные числа для Long. Но, как вы знаете, Java Long может содержать дополнительные символы, такие как +, L, _ и т.д. И это регулярное выражение не проверяет эти значения. Но если этого регулярного выражения недостаточно для вас, вы можете добавить для него дополнительные ограничения.

Ответ 12

Вы можете попробовать использовать регулярное выражение, чтобы проверить форму строки, прежде чем пытаться ее проанализировать?

Ответ 14

Простая реализация для проверки целого числа, подходящего для long, будет выглядеть так:

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

Он не обрабатывает восьмеричный, префикс 0x или около того, но это редко требуется.

Для ускорения выражения ".match" легко кодировать в цикле.