web-dev-qa-db-ja.com

try-catchなしで文字列がLongに解析可能かどうかを確認しますか?

文字列がlongに解析できない場合、Long.parseLong("string")はエラーをスローします。 try-catchを使用するよりも速く文字列を検証する方法はありますか?ありがとう

59
Serg

かなり複雑な正規表現を作成できますが、それだけの価値はありません。ここで例外を使用することは、まったく正常です。

それは自然な例外的な状況です。文字列に整数が存在するが、実際には何か他のものがあると仮定します。例外がスローされ、適切に処理される必要があります。

parseLongコードの内部を見ると、多くの異なる検証と操作があることがわかります。解析する前にすべてのことをしたい場合、パフォーマンスが低下します(数百万の数値の解析について話している場合は、それ以外の場合は重要ではありません)。したがって、例外を回避することでパフォーマンスを向上させるためにできる唯一のことは本当に必要な場合です:parseLong実装を独自の関数にコピーし、対応するすべてのケースで例外をスローする代わりにNaNを返します。

47
Roman

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

次のようなことができます

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

正規表現の使用-Stringが数字でいっぱいかどうかを確認します。しかし、あなたは何を得ることができますか?別のif条件?

10
ring bearer

文字列で表現されているデータのタイプを推測する必要がある場合があるため、これは有効な質問です。たとえば、大きなCSVをデータベースにインポートして、データ型を正確に表す必要がある場合があります。このような場合、Long.parseLongを呼び出して例外をキャッチすると、遅すぎる可能性があります。

次のコードは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;
    }
}
6
Woody

_Java.util.Scanner_を使用できます

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

これも範囲チェックなどを行います。もちろん_"99 bottles of beer"_ hasNextLong()と言うので、それを確認したい場合はonlylongが必要です追加のチェック。

5

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,".")){ ...
5
Andrew Norman

このケースは、入力フィールドがあり、文字列が有効な数値であるかどうかがわからないフォームやプログラムで一般的です。したがって、Java関数でtry/catchを使用することは、自分で関数を記述しようとする場合と比べてtry/catchがどのように機能するかを理解する場合に行うべき最善の方法です。 .NET仮想マシンでtry catchブロックをセットアップするために、オーバーヘッドの指示はありません。おそらくJavaでも同じです。 tryキーワードで使用される命令がある場合、これらは最小限であり、命令の大部分はcatch部分で使用されます。これは、まれなケースでのみ有効です。

したがって、自分でより高速な関数を作成できるように見えますが、すでに使用しているtry/catchメカニズムに打ち勝つために、Javaコンパイラよりも最適化する必要があります。数値解析は非常に一般的であるため、より最適化された機能は非常に最小限になります。

コンパイラと既に説明したJavaキャッチメカニズムを使用してタイミングテストを実行すると、おそらく上記のわずかなスローダウンに気付かないでしょう。

Java言語仕様を取得して例外をさらに理解すると、このような手法を使用することは、かなり大きく複雑な関数をラップするため、完全に受け入れられることがわかります。 try部分にCPUで追加の命令を追加することはそれほど大したことではありません。

3
Bob Holmes

parseより長いLong.parseLongよりもずっと速い方法があります。 not最適化されたメソッドの例をご覧になりたい場合は、parseLongをご覧ください:)

非ASCIIの「数字」を本当に考慮する必要がありますか?

複数のメソッド呼び出し基数を渡すのは本当に難しいでしょうか?

:)

正規表現を使用する方法はありません:数値が長すぎるかどうかを判断するのは難しいです:正規表現を使用して、9223372036854775807を長い間解析できるが、9223372036854775907はできないことをどのように判断しますか?

とはいえ、非常に高速な解析方法への答えはステートマシンであり、解析可能か解析するかは関係ありません。単純に、複雑な正規表現を受け入れる一般的な状態マシンではなく、ハードコードされたものです。

ロングを解析するメソッドと、ロングを解析できるかどうかを決定するメソッドを、両方とも書くことができますLong.parseLong()

今、あなたは何が欲しいですか?状態テスト方法?その場合、2倍の長さの計算を避けたい場合、状態テスト方法は望ましくないかもしれません。

コールをtry/catchでラップするだけです。

そして、ifデフォルトのLong.parseLongよりも高速なものが本当に必要な場合は、isあなたの問題に合わせて調整:10を底とする場合は10を底に、ASCIIをチェックしないでください。)。

2
SyntaxT3rr0r

これが正の値に役立つことを願っています。データベースの主キーの検証にこのメソッドを1回使用しました。

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

この正規表現を使用してみてください。

^(-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_など。そして、この正規表現はこれらの値を検証しません。ただし、この正規表現では不十分な場合は、追加の制限を追加できます。

2
Aliaksei Mychko

Stringが有効なlong値かどうかを確認する唯一の方法だと思います。しかし、最大の長い価値を念頭に置いて、それを行うメソッドを自分で実装することができます。

2
cd1

正規表現を使用して、解析する前に文字列の形式を確認できますか?

0
Moonshield

グアバ Longs.tryParse( "string") は、解析に失敗した場合に例外をスローする代わりにnullを返します。しかし、この方法は ベータとしてマークされています 現在です。

0
Anton Yuriev

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

8進数、0xプレフィックスなどは処理しませんが、それはほとんど要件ではありません。

速度を上げるために、「。match」式はループで簡単にコーディングできます。

0
Florian F