Javaで文字列が整数を表しているかどうかを確認する最良の方法は何ですか?

StackOverflow https://stackoverflow.com/questions/237159

  •  04-07-2019
  •  | 
  •  

質問

私は通常、次のイディオムを使用して、文字列を整数に変換できるかどうかを確認します。

public boolean isInteger( String input ) {
    try {
        Integer.parseInt( input );
        return true;
    }
    catch( Exception e ) {
        return false;
    }
}

それともこれはちょっとハックっぽいと思うのは私だけでしょうか?もっと良い方法は何でしょうか?


私の答えを参照してください(ベンチマーク付き、に基づいています) 前の答え による スパイクによるコーディング)なぜ立場を逆転して受け入れたのかを知るために ジョナス・クレミングの答え この問題に。このオリジナルのコードは、実装が速く、保守しやすいため、ほとんどの人が使用すると思いますが、非整数データが​​提供されると桁違いに遅くなります。

役に立ちましたか?

解決

潜在的なオーバーフローの問題を気にしない場合、この関数は使用するよりも約 20 ~ 30 倍高速に実行されます。 Integer.parseInt().

public static boolean isInteger(String str) {
    if (str == null) {
        return false;
    }
    int length = str.length();
    if (length == 0) {
        return false;
    }
    int i = 0;
    if (str.charAt(0) == '-') {
        if (length == 1) {
            return false;
        }
        i = 1;
    }
    for (; i < length; i++) {
        char c = str.charAt(i);
        if (c < '0' || c > '9') {
            return false;
        }
    }
    return true;
}

他のヒント

あなたはそれを持っていますが、あなたは捕まえるだけでいいです NumberFormatException.

ベンチマーク後に人々がまだここを訪問し、正規表現に対して偏見を持つ可能性があるため...そこで、正規表現のコンパイル済みバージョンを使用して、ベンチマークの更新バージョンを提供します。前のベンチマークとは対照的に、このベンチマークは Regex ソリューションが実際に一貫して優れたパフォーマンスを示していることを示しています。

Bill the Lizard からコピーされ、コンパイルされたバージョンで更新されました。

private final Pattern pattern = Pattern.compile("^-?\\d+$");

public void runTests() {
    String big_int = "1234567890";
    String non_int = "1234XY7890";

    long startTime = System.currentTimeMillis();
    for(int i = 0; i < 100000; i++)
            IsInt_ByException(big_int);
    long endTime = System.currentTimeMillis();
    System.out.print("ByException - integer data: ");
    System.out.println(endTime - startTime);

    startTime = System.currentTimeMillis();
    for(int i = 0; i < 100000; i++)
            IsInt_ByException(non_int);
    endTime = System.currentTimeMillis();
    System.out.print("ByException - non-integer data: ");
    System.out.println(endTime - startTime);

    startTime = System.currentTimeMillis();
    for(int i = 0; i < 100000; i++)
            IsInt_ByRegex(big_int);
    endTime = System.currentTimeMillis();
    System.out.print("\nByRegex - integer data: ");
    System.out.println(endTime - startTime);

    startTime = System.currentTimeMillis();
    for(int i = 0; i < 100000; i++)
            IsInt_ByRegex(non_int);
    endTime = System.currentTimeMillis();
    System.out.print("ByRegex - non-integer data: ");
    System.out.println(endTime - startTime);

    startTime = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++)
            IsInt_ByCompiledRegex(big_int);
    endTime = System.currentTimeMillis();
    System.out.print("\nByCompiledRegex - integer data: ");
    System.out.println(endTime - startTime);

    startTime = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++)
            IsInt_ByCompiledRegex(non_int);
    endTime = System.currentTimeMillis();
    System.out.print("ByCompiledRegex - non-integer data: ");
    System.out.println(endTime - startTime);


    startTime = System.currentTimeMillis();
    for(int i = 0; i < 100000; i++)
            IsInt_ByJonas(big_int);
    endTime = System.currentTimeMillis();
    System.out.print("\nByJonas - integer data: ");
    System.out.println(endTime - startTime);

    startTime = System.currentTimeMillis();
    for(int i = 0; i < 100000; i++)
            IsInt_ByJonas(non_int);
    endTime = System.currentTimeMillis();
    System.out.print("ByJonas - non-integer data: ");
    System.out.println(endTime - startTime);
}

private boolean IsInt_ByException(String str)
{
    try
    {
        Integer.parseInt(str);
        return true;
    }
    catch(NumberFormatException nfe)
    {
        return false;
    }
}

private boolean IsInt_ByRegex(String str)
{
    return str.matches("^-?\\d+$");
}

private boolean IsInt_ByCompiledRegex(String str) {
    return pattern.matcher(str).find();
}

public boolean IsInt_ByJonas(String str)
{
    if (str == null) {
            return false;
    }
    int length = str.length();
    if (length == 0) {
            return false;
    }
    int i = 0;
    if (str.charAt(0) == '-') {
            if (length == 1) {
                    return false;
            }
            i = 1;
    }
    for (; i < length; i++) {
            char c = str.charAt(i);
            if (c <= '/' || c >= ':') {
                    return false;
            }
    }
    return true;
}

結果:

ByException - integer data: 45
ByException - non-integer data: 465

ByRegex - integer data: 272
ByRegex - non-integer data: 131

ByCompiledRegex - integer data: 45
ByCompiledRegex - non-integer data: 26

ByJonas - integer data: 8
ByJonas - non-integer data: 2

簡単なベンチマークを実行しました。複数のメソッドをポップバックし始めたり、JVM が実行スタックを適切な位置に取得するために多くの作業を実行したりしない限り、例外は実際にはそれほどコストがかかりません。同じやり方を続けていれば、パフォーマンスが悪いわけではありません。

 public void RunTests()
 {
     String str = "1234567890";

     long startTime = System.currentTimeMillis();
     for(int i = 0; i < 100000; i++)
         IsInt_ByException(str);
     long endTime = System.currentTimeMillis();
     System.out.print("ByException: ");
     System.out.println(endTime - startTime);

     startTime = System.currentTimeMillis();
     for(int i = 0; i < 100000; i++)
         IsInt_ByRegex(str);
     endTime = System.currentTimeMillis();
     System.out.print("ByRegex: ");
     System.out.println(endTime - startTime);

     startTime = System.currentTimeMillis();
     for(int i = 0; i < 100000; i++)
         IsInt_ByJonas(str);
     endTime = System.currentTimeMillis();
     System.out.print("ByJonas: ");
     System.out.println(endTime - startTime);
 }

 private boolean IsInt_ByException(String str)
 {
     try
     {
         Integer.parseInt(str);
         return true;
     }
     catch(NumberFormatException nfe)
     {
         return false;
     }
 }

 private boolean IsInt_ByRegex(String str)
 {
     return str.matches("^-?\\d+$");
 }

 public boolean IsInt_ByJonas(String str)
 {
     if (str == null) {
             return false;
     }
     int length = str.length();
     if (length == 0) {
             return false;
     }
     int i = 0;
     if (str.charAt(0) == '-') {
             if (length == 1) {
                     return false;
             }
             i = 1;
     }
     for (; i < length; i++) {
             char c = str.charAt(i);
             if (c <= '/' || c >= ':') {
                     return false;
             }
     }
     return true;
 }

出力:

例外により:31

正規表現による:453 (注:毎回パターンを再コンパイルします)

投稿者ジョナス:16

Jonas K のソリューションが最も堅牢であることにも同意します。彼が勝ったようです:)

org.apache.commons.lang.StringUtils.isNumeric 

ただし、Java の標準ライブラリにはそのようなユーティリティ関数がありません。

Apache Commons はすべての Java プログラマーにとって「必須」だと思います

残念ながらまだ Java5 に移植されていません

それは部分的に、「整数に変換できる」が何を意味するかによって異なります。

「Java で int に変換できる」ということを意味するのであれば、Jonas からの答えは良いスタートではありますが、完全に仕事を完了するものではありません。たとえば、999999999999999999999999999999 を渡します。メソッドの最後に、独自の質問からの通常の try/catch 呼び出しを追加します。

文字ごとのチェックは、「まったく整数ではない」ケースを効率的に拒否し、「整数だが Java が処理できない」ケースは、より遅い例外ルートによってキャッチされるままにします。あなた できた これも少しずつ手動で行いますが、 多く より複雑。

正規表現について 1 つだけコメントします。ここに挙げた例はすべて間違っています。正規表現を使用したい場合は、パターンのコンパイルに時間がかかることを忘れないでください。これ:

str.matches("^-?\\d+$")

そしてこれも:

Pattern.matches("-?\\d+", input);

すべてのメソッド呼び出しでパターンをコンパイルします。正しく使用するには、次の手順に従います。

import java.util.regex.Pattern;

/**
 * @author Rastislav Komara
 */
public class NaturalNumberChecker {
    public static final Pattern PATTERN = Pattern.compile("^\\d+$");

    boolean isNaturalNumber(CharSequence input) {
        return input != null && PATTERN.matcher(input).matches();
    }
}

rally25rsの回答からコードをコピーし、非整数データのテストをいくつか追加しました。結果は、間違いなく Jonas Klemming が投稿した方法を支持しています。最初に投稿した Exception メソッドの結果は、整数データが​​ある場合はかなり良好ですが、そうでない場合は最悪になります。一方、(多くの人が使用すると思われる) RegEx ソリューションの結果は最悪です。だった 一貫して 悪い。見る フェリペの答え コンパイルされた正規表現の例では、これははるかに高速です。

public void runTests()
{
    String big_int = "1234567890";
    String non_int = "1234XY7890";

    long startTime = System.currentTimeMillis();
    for(int i = 0; i < 100000; i++)
        IsInt_ByException(big_int);
    long endTime = System.currentTimeMillis();
    System.out.print("ByException - integer data: ");
    System.out.println(endTime - startTime);

    startTime = System.currentTimeMillis();
    for(int i = 0; i < 100000; i++)
        IsInt_ByException(non_int);
    endTime = System.currentTimeMillis();
    System.out.print("ByException - non-integer data: ");
    System.out.println(endTime - startTime);

    startTime = System.currentTimeMillis();
    for(int i = 0; i < 100000; i++)
        IsInt_ByRegex(big_int);
    endTime = System.currentTimeMillis();
    System.out.print("\nByRegex - integer data: ");
    System.out.println(endTime - startTime);

    startTime = System.currentTimeMillis();
    for(int i = 0; i < 100000; i++)
        IsInt_ByRegex(non_int);
    endTime = System.currentTimeMillis();
    System.out.print("ByRegex - non-integer data: ");
    System.out.println(endTime - startTime);

    startTime = System.currentTimeMillis();
    for(int i = 0; i < 100000; i++)
        IsInt_ByJonas(big_int);
    endTime = System.currentTimeMillis();
    System.out.print("\nByJonas - integer data: ");
    System.out.println(endTime - startTime);

    startTime = System.currentTimeMillis();
    for(int i = 0; i < 100000; i++)
        IsInt_ByJonas(non_int);
    endTime = System.currentTimeMillis();
    System.out.print("ByJonas - non-integer data: ");
    System.out.println(endTime - startTime);
}

private boolean IsInt_ByException(String str)
{
    try
    {
        Integer.parseInt(str);
        return true;
    }
    catch(NumberFormatException nfe)
    {
        return false;
    }
}

private boolean IsInt_ByRegex(String str)
{
    return str.matches("^-?\\d+$");
}

public boolean IsInt_ByJonas(String str)
{
    if (str == null) {
            return false;
    }
    int length = str.length();
    if (length == 0) {
            return false;
    }
    int i = 0;
    if (str.charAt(0) == '-') {
            if (length == 1) {
                    return false;
            }
            i = 1;
    }
    for (; i < length; i++) {
            char c = str.charAt(i);
            if (c <= '/' || c >= ':') {
                    return false;
            }
    }
    return true;
}

結果:

ByException - integer data: 47
ByException - non-integer data: 547

ByRegex - integer data: 390
ByRegex - non-integer data: 313

ByJonas - integer data: 0
ByJonas - non-integer data: 16

グアババージョンがあります:

import com.google.common.primitives.Ints;

Integer intValue = Ints.tryParse(stringValue);

文字列の解析に失敗した場合は、例外をスローする代わりに null を返します。

これは短いですが、短いほど良いとは限りません (範囲外の整数値は捕捉されません)。 Danatelのコメントで指摘されているように):

input.matches("^-?\\d+$");

個人的には、実装はヘルパー メソッドに詰め込まれており、長さよりも正確さが優先されるため、私はあなたが持っているもののようなものを使用します(ベースをキャッチすることを除いて) Exception クラスではなく NumberFormatException).

string クラスのmatchesメソッドを使用できます。[0-9] は取り得るすべての値を表し、+ は少なくとも 1 文字の長さである必要があることを意味し、* は 0 文字以上の長さであることができることを意味します。

boolean isNumeric = yourString.matches("[0-9]+"); // 1 or more characters long, numbers only
boolean isNumeric = yourString.matches("[0-9]*"); // 0 or more characters long, numbers only

これは、Jonas Klemming の回答の Java 8 バリエーションです。

public static boolean isInteger(String str) {
    return str != null && str.length() > 0 &&
         IntStream.range(0, str.length()).allMatch(i -> i == 0 && (str.charAt(i) == '-' || str.charAt(i) == '+')
                  || Character.isDigit(str.charAt(i)));
}

テストコード:

public static void main(String[] args) throws NoSuchAlgorithmException, UnsupportedEncodingException {
    Arrays.asList("1231231", "-1232312312", "+12313123131", "qwqe123123211", "2", "0000000001111", "", "123-", "++123",
            "123-23", null, "+-123").forEach(s -> {
        System.out.printf("%15s %s%n", s, isInteger(s));
    });
}

テストコードの結果:

        1231231 true
    -1232312312 true
   +12313123131 true
  qwqe123123211 false
              2 true
  0000000001111 true
                false
           123- false
          ++123 false
         123-23 false
           null false
          +-123 false

String 配列に純粋な Integer と String が含まれている場合、以下のコードは機能するはずです。最初の文字だけを見る必要があります。例えば["4","44","abc","77","絆"]

if (Character.isDigit(string.charAt(0))) {
    //Do something with int
}

を使用することもできます。 スキャナー クラスと使用 hasNextInt() - これにより、float などの他の型もテストできるようになります。

どうでしょうか:

return Pattern.matches("-?\\d+", input);

チェックするだけです NumberFormatException:-

 String value="123";
 try  
 {  
    int s=Integer.parseInt(any_int_val);
    // do something when integer values comes 
 }  
 catch(NumberFormatException nfe)  
 {  
          // do something when string values comes 
 }  

Apache ユーティリティを試してみてください

NumberUtils.isNumber( myText)

ここのJavadocを参照してください

おそらくユースケースも考慮する必要があります。

ほとんどの場合、数値が有効であることが期待される場合、無効な数値を変換しようとしたときに例外をキャッチしてもパフォーマンスのオーバーヘッドが発生するだけです。いくつかに電話をかけながら、 isInteger() メソッドを使用して変換します Integer.parseInt() 意思 いつも 有効な数値に対してパフォーマンスのオーバーヘッドが発生します。文字列は 2 回解析されます (1 回はチェックによって、もう 1 回は変換によって)。

これはの修正版です ジョナス' 文字列が整数にキャストされる範囲内にあるかどうかをチェックするコード。

public static boolean isInteger(String str) {
    if (str == null) {
        return false;
    }
    int length = str.length();
    int i = 0;

    // set the length and value for highest positive int or lowest negative int
    int maxlength = 10;
    String maxnum = String.valueOf(Integer.MAX_VALUE);
    if (str.charAt(0) == '-') { 
        maxlength = 11;
        i = 1;
        maxnum = String.valueOf(Integer.MIN_VALUE);
    }  

    // verify digit length does not exceed int range
    if (length > maxlength) { 
        return false; 
    }

    // verify that all characters are numbers
    if (maxlength == 11 && length == 1) {
        return false;
    }
    for (int num = i; num < length; num++) {
        char c = str.charAt(num);
        if (c < '0' || c > '9') {
            return false;
        }
    }

    // verify that number value is within int range
    if (length == maxlength) {
        for (; i < length; i++) {
            if (str.charAt(i) < maxnum.charAt(i)) {
                return true;
            }
            else if (str.charAt(i) > maxnum.charAt(i)) {
                return false;
            }
        }
    }
    return true;
}

Android API を使用している場合は、以下を使用できます。

TextUtils.isDigitsOnly(str);

別のオプション:

private boolean isNumber(String s) {
    boolean isNumber = true;
    for (char c : s.toCharArray()) {
        isNumber = isNumber && Character.isDigit(c);
    }
    return isNumber;
}

文字列が int 型に適合する整数を表しているかどうかを確認したい場合は、jonas の回答に少し変更を加えて、Integer.MAX_VALUE より大きい、または Integer.MIN_VALUE より小さい整数を表す文字列が返されるようにしました。間違い。例えば:「3147483647」は、3147483647 が 2147483647 より大きいため false を返します。同様に、「-2147483649」も、-2147483649 が -2147483648 より小さいため、false を返します。

public static boolean isInt(String s) {
  if(s == null) {
    return false;
  }
  s = s.trim(); //Don't get tricked by whitespaces.
  int len = s.length();
  if(len == 0) {
    return false;
  }
  //The bottom limit of an int is -2147483648 which is 11 chars long.
  //[note that the upper limit (2147483647) is only 10 chars long]
  //Thus any string with more than 11 chars, even if represents a valid integer, 
  //it won't fit in an int.
  if(len > 11) {
    return false;
  }
  char c = s.charAt(0);
  int i = 0;
  //I don't mind the plus sign, so "+13" will return true.
  if(c == '-' || c == '+') {
    //A single "+" or "-" is not a valid integer.
    if(len == 1) {
      return false;
    }
    i = 1;
  }
  //Check if all chars are digits
  for(; i < len; i++) {
    c = s.charAt(i);
    if(c < '0' || c > '9') {
      return false;
    }
  }
  //If we reached this point then we know for sure that the string has at
  //most 11 chars and that they're all digits (the first one might be a '+'
  // or '-' thought).
  //Now we just need to check, for 10 and 11 chars long strings, if the numbers
  //represented by the them don't surpass the limits.
  c = s.charAt(0);
  char l;
  String limit;
  if(len == 10 && c != '-' && c != '+') {
    limit = "2147483647";
    //Now we are going to compare each char of the string with the char in
    //the limit string that has the same index, so if the string is "ABC" and
    //the limit string is "DEF" then we are gonna compare A to D, B to E and so on.
    //c is the current string's char and l is the corresponding limit's char
    //Note that the loop only continues if c == l. Now imagine that our string
    //is "2150000000", 2 == 2 (next), 1 == 1 (next), 5 > 4 as you can see,
    //because 5 > 4 we can guarantee that the string will represent a bigger integer.
    //Similarly, if our string was "2139999999", when we find out that 3 < 4,
    //we can also guarantee that the integer represented will fit in an int.
    for(i = 0; i < len; i++) {
      c = s.charAt(i);
      l = limit.charAt(i);
      if(c > l) {
        return false;
      }
      if(c < l) {
        return true;
      }
    }
  }
  c = s.charAt(0);
  if(len == 11) {
    //If the first char is neither '+' nor '-' then 11 digits represent a 
    //bigger integer than 2147483647 (10 digits).
    if(c != '+' && c != '-') {
      return false;
    }
    limit = (c == '-') ? "-2147483648" : "+2147483647";
    //Here we're applying the same logic that we applied in the previous case
    //ignoring the first char.
    for(i = 1; i < len; i++) {
      c = s.charAt(i);
      l = limit.charAt(i);
      if(c > l) {
        return false;
      }
      if(c < l) {
        return true;
      }
    }
  }
  //The string passed all tests, so it must represent a number that fits
  //in an int...
  return true;
}
is_number = true;
try {
  Integer.parseInt(mystr)
} catch (NumberFormatException  e) {
  is_number = false;
}

あなたがやったことは機能しますが、常にそのようにチェックする必要はありません。例外のスローは「例外的な」状況 (おそらくあなたの場合には当てはまるかもしれません) のために予約されるべきであり、パフォーマンスの点で非常にコストがかかります。

Number number;
try {
    number = NumberFormat.getInstance().parse("123");
} catch (ParseException e) {
    //not a number - do recovery.
    e.printStackTrace();
}
//use number

これは正の整数の場合にのみ機能します。

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

これは私にとってはうまくいきます。単に文字列がプリミティブであるか数値であるかを識別するためです。

private boolean isPrimitive(String value){
        boolean status=true;
        if(value.length()<1)
            return false;
        for(int i = 0;i<value.length();i++){
            char c=value.charAt(i);
            if(Character.isDigit(c) || c=='.'){

            }else{
                status=false;
                break;
            }
        }
        return status;
    }

すべての int 文字をチェックするには、二重否定を使用するだけです。

if (!searchString.matches("[^0-9]+$")) ...

[^0-9]+$ は、整数ではない文字があるかどうかをチェックするため、それが true の場合、テストは失敗します。そうしないと成功します。

これが役に立つかもしれません:

public static boolean isInteger(String self) {
    try {
        Integer.valueOf(self.trim());
        return true;
    } catch (NumberFormatException nfe) {
        return false;
    }
}

以下でわかるように、常に安全に解析できるため、例外に遭遇するリスクはゼロだと思います。 intString その逆ではありません。

それで:

  1. あなた チェック 文字列のすべての文字スロットが少なくとも1つの文字に一致する場合 {"0","1","2","3","4","5","6","7","8","9"}.

    if(aString.substring(j, j+1).equals(String.valueOf(i)))
    
  2. あなた 上記の文字をスロットで遭遇したすべての時間。

    digits++;
    
  3. そして最後にあなたは チェック 文字が指定された文字列の長さと等しいため、整数に遭遇した時間が整数に遭遇した場合。

    if(digits == aString.length())
    

そして実際には次のようになります。

    String aString = "1234224245";
    int digits = 0;//count how many digits you encountered
    for(int j=0;j<aString.length();j++){
        for(int i=0;i<=9;i++){
            if(aString.substring(j, j+1).equals(String.valueOf(i)))
                    digits++;
        }
    }
    if(digits == aString.length()){
        System.out.println("It's an integer!!");
        }
    else{
        System.out.println("It's not an integer!!");
    }

    String anotherString = "1234f22a4245";
    int anotherDigits = 0;//count how many digits you encountered
    for(int j=0;j<anotherString.length();j++){
        for(int i=0;i<=9;i++){
            if(anotherString.substring(j, j+1).equals(String.valueOf(i)))
                    anotherDigits++;
        }
    }
    if(anotherDigits == anotherString.length()){
        System.out.println("It's an integer!!");
        }
    else{
        System.out.println("It's not an integer!!");
    }

結果は次のとおりです。

整数ですよ!!

整数じゃないよ!!

同様に、 String です float または double しかし、そのような場合には遭遇しなければなりません 唯一 。 文字列内の (ドット)、もちろんチェックします digits == (aString.length()-1)

繰り返しますが、ここでも解析例外が発生するリスクはありませんが、数値を含むことがわかっている文字列を解析する予定がある場合 (たとえば、 整数 データ型)、最初にデータ型に適合するかどうかを確認する必要があります。それ以外の場合はキャストする必要があります。

お役に立てれば幸いです

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top