24

この質問の変種が以前に頻繁に尋ねられたことは知っていますが(たとえば、ここここを参照)、これはそれらの正確な複製ではありません。

aが数値かどうかを確認しStringたいので、数値の場合は。として保存しdoubleます。これを行うにはいくつかの方法がありますが、それらはすべて私の目的には不適切のようです。

Double.parseDouble(s)1つの解決策は、または同様に使用することnew BigDecimal(s)です。ただし、カンマが存在する場合、これらのソリューションは機能しません(したがって、「1,234」は例外を引き起こします)。もちろん、これらの手法を使用する前にすべてのコンマを取り除くことはできますが、それは他のロケールで多くの問題を引き起こすように思われます。

Apache Commonsを見ましたNumberUtils.isNumber(s)が、同じコンマの問題があります。

私はNumberFormatまたはを検討DecimalFormatしましたが、それらはあまりにも寛大であるように見えました。たとえば、「1A」は数値ではないことを示すのではなく、「1」にフォーマットされます。さらに、「127.0.0.1」のようなものは、数字ではないことを示すのではなく、127としてカウントされます。

私の要件は私が最初にこれを行うほどエキゾチックではないように感じますが、どのソリューションも私が必要とするものを正確に実行しません。何が必要か正確にはわからないと思いますが(そうでなければ、独自のパーサーを作成できます)、上記の解決策が示された理由で機能しないことはわかっています。解決策はありますか、それとも必要なものを正確に把握して独自のコードを作成する必要がありますか?

4

15 に答える 15

16

かなり奇妙に聞こえますが、私はこの答えに従って、を使用しようとしますjava.util.Scanner

Scanner scanner = new Scanner(input);
if (scanner.hasNextInt())
    System.out.println(scanner.nextInt());
else if (scanner.hasNextDouble())
    System.out.println(scanner.nextDouble());
else
    System.out.println("Not a number");

、、、などの入力の場合1A127.0.0.1次の出力が得られます1,2346.02e-23

Not a number
Not a number
1234
6.02E-23

Scanner.useLocale目的のロケールに変更するために使用できます。

于 2012-02-08T00:21:23.840 に答える
4

必要なロケールを指定できます。

NumberFormat nf = NumberFormat.getInstance(Locale.GERMAN);
double myNumber = nf.parse(myString).doubleValue();

ドイツ語ロケールには小数点記号としてコンマがあるため、これは例で機能するはずです。

于 2012-02-07T09:11:39.240 に答える
4

ParsePositionは、NumberFormat.parse操作で文字列が完全に消費されているかどうかのチェックとして使用できます。文字列が消費された場合、「1A」の状況は発生しません。そうでない場合は、そうし、それに応じて動作することができます。解決策の概要についてはこちらを、ParsePositionオプションのために修正されないとしてクローズされた関連するJDKのバグについてはこちらをご覧ください。

于 2012-02-07T15:59:04.610 に答える
3

残念ながら、Double.parseDouble(s)または新しいBigDecimal(s)が最良の選択肢のようです。

ローカリゼーションの懸念を引用しますが、残念ながら、ユーザーによる指定なしですべてのロケールを確実にサポートする方法はありません。それは不可能です。

カンマとピリオドのどちらが最初に使用されているかを確認することで、使用されているスキームについて推論できる場合がありますが、両方が使用されている場合、これが常に可能であるとは限りません。より多くの状況で機能する可能性があるが、悪い結果をもたらす可能性のあるシステムに依存しようとするよりも、特定の状況で確実に機能することがわかっているシステムを用意する方がよい...

123,456という数字は何を表していますか?123456または123.456?

ユーザーが指定したロケールに応じて、コンマ、スペース、またはピリオドを削除するだけです。デフォルトでは、スペースとコンマを削除します。厳密にしたい場合は、コンマまたはスペースのみを削除し、両方は削除せず、ピリオドがある場合はピリオドの前のみを削除します。また、3つに適切に配置されているかどうかを手動で確認するのも非常に簡単です。実際、ここではカスタムパーサーが最も簡単な場合があります。

これが概念実証のビットです。それは少し(非常に)厄介ですが、私はそれがうまくいくと思います、そしてあなたはとにかくアイデアを得るでしょう:)。

public class StrictNumberParser {
  public double parse(String numberString) throws NumberFormatException {
    numberString = numberString.trim();
    char[] numberChars = numberString.toCharArray();

    Character separator = null;
    int separatorCount = 0;
    boolean noMoreSeparators = false;
    for (int index = 1; index < numberChars.length; index++) {
      char character = numberChars[index];

      if (noMoreSeparators || separatorCount < 3) {
        if (character == '.') {
          if (separator != null) {
            throw new NumberFormatException();
          } else {
            noMoreSeparators = true;
          }
        } else if (separator == null && (character == ',' || character == ' ')) {
          if (noMoreSeparators) {
            throw new NumberFormatException();
          }
          separator = new Character(character);
          separatorCount = -1;
        } else if (!Character.isDigit(character)) {
          throw new NumberFormatException();
        }

        separatorCount++;
      } else {
        if (character == '.') {
          noMoreSeparators = true;
        } else if (separator == null) {
          if (Character.isDigit(character)) {
            noMoreSeparators = true;
          } else if (character == ',' || character == ' ') {
            separator = new Character(character);
          } else {
            throw new NumberFormatException();
          }
        } else if (!separator.equals(character)) {
          throw new NumberFormatException();
        }

        separatorCount = 0;
      }
    }

    if (separator != null) {
      if (!noMoreSeparators && separatorCount != 3) {
        throw new NumberFormatException();
      }
      numberString = numberString.replaceAll(separator.toString(), "");
    }

    return Double.parseDouble(numberString);
  }

  public void testParse(String testString) {
    try {
      System.out.println("result: " + parse(testString));
    } catch (NumberFormatException e) {
      System.out.println("Couldn't parse number!");
    }
  }

  public static void main(String[] args) {
    StrictNumberParser p = new StrictNumberParser();
    p.testParse("123 45.6");
    p.testParse("123 4567.8");
    p.testParse("123 4567");
    p.testParse("12 45");
    p.testParse("123 456 45");
    p.testParse("345.562,346");
    p.testParse("123 456,789");
    p.testParse("123,456,789");
    p.testParse("123 456 789.52");
    p.testParse("23,456,789");
    p.testParse("3,456,789");
    p.testParse("123 456.12");
    p.testParse("1234567.8");
  }
}

編集:明らかにこれは科学的記数法を認識するために拡張する必要がありますが、これは十分に単純である必要があります。特に、eの後に実際に何も検証する必要がないため、形式が正しくない場合はparseDoubleを失敗させることができます。

また、これを使用してNumberFormatを適切に拡張することもお勧めします。解析された数値用のgetSeparator()と、目的の出力形式を提供するためのsetSeparatorがあります...この種のローカリゼーションは処理されますが、小数の「、」をサポートするには、さらに多くの作業を行う必要があります。

于 2012-02-10T11:33:02.860 に答える
3

すべての要件を満たしているかどうかはわかりませんが、ここにあるコードは正しい方向を示している可能性がありますか?

記事から:

要約すると、適切な入力処理の手順は次のとおりです。

  1. 適切なNumberFormatを取得し、ParsePosition変数を定義します。
  2. ParsePositionインデックスをゼロに設定します。
  3. parse(String source、ParsePosition parsePosition)を使用して入力値を解析します。
  4. 入力長とParsePositionインデックス値が一致しない場合、または解析された数値がnullの場合は、エラー操作を実行します。
  5. それ以外の場合、値は検証に合格しました。
于 2012-02-08T21:28:45.733 に答える
3

これは興味深い問題です。しかし、おそらくそれは少しオープンエンドですか?10進数、16進数、または何を識別するために特に探していますか?私は10進数を想定しています。通貨はどうですか?それは重要ですか?それとも単なる数字ですか。

いずれにせよ、数値形式の欠点を生かすことができると思います。「1A」のようなものは1と解釈されますので、フォーマットして元の文字列と比較して結果を確認してみませんか?

public static boolean isNumber(String s){
    try{
        Locale l = Locale.getDefault();
        DecimalFormat df = new DecimalFormat("###.##;-##.##");
        Number n = df.parse(s);
        String sb = df.format(n);
        return sb.equals(s);
    }
    catch(Exception e){
        return false;
    }
} 

どう思いますか?

于 2012-02-11T15:39:18.873 に答える
3

これは本当に面白いです、そして私は人々がそれを過度に複雑にしようとしていると思います。私は本当にこれをルールで分類します:

1)科学的記数法を確認します(すべての数字、コンマ、ピリオド、-/ +であり、「e」が含まれているパターンと一致しますか?)-ある場合は、必要に応じて解析します

2)有効な数字(0〜9、。

ここで機能するショートカットがわかりません。ブルートフォースアプローチを採用するだけです。プログラミングのすべてが完全にエレガントであるとは限りません(またはそうである必要があります)。

于 2012-02-11T19:32:26.833 に答える
3

私の理解では、可能な限り厳密な解釈を維持しながら、西洋/ラテン語をカバーしたいと考えています。したがって、ここで行っているのは、DecimalFormatSymbolsに、グループ化、10進数、負、およびゼロの区切り文字が何であるかを教えてもらい、それらをDoubleが認識する記号と交換することです。

それはどのように機能しますか?

米国では、「1A」、「127.100.100.100」を拒否し、「1.47E-9」を受け入れます。

ドイツではまだ「1A」を拒否します

「1,024.00」を受け入れますが、1.024として正しく解釈します。同様に、127100100100.0として「127.100.100.100」を受け入れます。

実際、ドイツ語ロケールは「1,47E-9」を正しく識別して解析します

別のロケールで問題が発生した場合はお知らせください。

import java.util.Locale;
import java.text.DecimalFormatSymbols;

public class StrictNumberFormat {

public static boolean isDouble(String s, Locale l) {
    String clean = convertLocaleCharacters(s,l);

    try {
        Double.valueOf(clean);
        return true;
    } catch (NumberFormatException nfe) {
        return false;
    }
}

public static double doubleValue(String s, Locale l) {
    return Double.valueOf(convertLocaleCharacters(s,l));
}

public static boolean isDouble(String s) {
    return isDouble(s,Locale.getDefault());
}

public static double doubleValue(String s) {
    return doubleValue(s,Locale.getDefault());
}

private static String convertLocaleCharacters(String number, Locale l) {
    DecimalFormatSymbols symbols = new DecimalFormatSymbols(l);
    String grouping = getUnicodeRepresentation( symbols.getGroupingSeparator() );
    String decimal = getUnicodeRepresentation( symbols.getDecimalSeparator() );
    String negative = getUnicodeRepresentation( symbols.getMinusSign() );
    String zero = getUnicodeRepresentation( symbols.getZeroDigit() );

    String clean = number.replaceAll(grouping, "");
    clean = clean.replaceAll(decimal, ".");
    clean = clean.replaceAll(negative, "-");
    clean = clean.replaceAll(zero, "0");

    return clean;
}

private static String getUnicodeRepresentation(char ch) {
    String unicodeString = Integer.toHexString(ch); //ch implicitly promoted to int
    while(unicodeString.length()<4) unicodeString = "0"+unicodeString;

    return "\\u"+unicodeString;
}

}
于 2012-02-11T21:35:47.287 に答える
3

手動で行うのが最善です。あなたが数として受け入れることができるものを理解し、他のすべてを無視してください:

   import java.lang.NumberFormatException;
   import java.util.regex.Pattern;
   import java.util.regex.Matcher;

   public class ParseDouble {
   public static void main(String[] argv) {

       String line = "$$$|%|#|1A|127.0.0.1|1,344|95|99.64";

       for (String s : line.split("\\|")) {
           try {
               System.out.println("parsed: " + 
               any2double(s)
                       );

           }catch (NumberFormatException ne) {
               System.out.println(ne.getMessage());
           }
       }   
   }
   public static double any2double(String input) throws NumberFormatException {

       double out =0d;

       Pattern special         = Pattern.compile("[^a-zA-Z0-9\\.,]+");
       Pattern letters         = Pattern.compile("[a-zA-Z]+");
       Pattern comma           = Pattern.compile(",");
       Pattern allDigits       = Pattern.compile("^[0-9]+$");
       Pattern singleDouble    = Pattern.compile("^[0-9]+\\.[0-9]+$");

       Matcher[] goodCases = new Matcher[]{
           allDigits.matcher(input),
           singleDouble.matcher(input)
       };           

       Matcher[] nanCases = new Matcher[]{
           special.matcher(input),
           letters.matcher(input)
       };


       // maybe cases 
       if (comma.matcher(input).find()){
           out = Double.parseDouble( 
               comma.matcher(input).replaceFirst("."));
           return out;

       }

       for (Matcher m : nanCases) {
           if (m.find()) {
               throw new NumberFormatException("Bad input "+input);
           }
       }

       for (Matcher m : goodCases) {

           if (m.find()) {
               try {
                   out = Double.parseDouble(input);
                   return out;
               } catch (NumberFormatException ne){
                   System.out.println(ne.getMessage());
               }
           }
       }
       throw new NumberFormatException("Could not parse "+input);
   }
   }
于 2012-02-12T11:31:44.063 に答える
1

カンマ区切りの10進数である文字列番号をdoubleに変換する場合は、DecimalSeparator+DecimalFormalSymbolsを使用できます。

final double strToDouble(String str, char separator){
    DecimalFormatSymbols s = new DecimalFormatSymbols();
    s.setDecimalSeparator(separator);
    DecimalFormat df = new DecimalFormat();

    double num = 0;
    df.setDecimalFormatSymbols(s);
    try{
        num = ((Double) df.parse(str)).doubleValue();
    }catch(ClassCastException | ParseException ex){
        // if you want, you could add something here to 
        // indicate the string is not double
    }  
    return num;
}

さて、それをテストしましょう:

    String a = "1.2";
    String b = "2,3";
    String c = "A1";
    String d = "127.0.0.1";

    System.out.println("\"" + a + "\" = " + strToDouble(a, ','));
    System.out.println("\"" + a + "\" (with '.' as separator) = " 
            + strToDouble(a, '.'));
    System.out.println("\"" + b + "\" = " + strToDouble(b, ','));
    System.out.println("\"" + c + "\" = " + strToDouble(c, ','));
    System.out.println("\"" + d + "\" = " + strToDouble(d, ','));

上記のコードを実行すると、次のように表示されます。

"1.2" = 0.0
"1.2" (with '.' as separator) = 1.2
"2,3" = 2.3
"A1" = 0.0
"127.0.0.1" = 0.0
于 2012-02-10T09:48:13.353 に答える
1

ロケールを正しく設定すると、組み込みはparseDoubleコンマで機能します。例はここにあります。

于 2011-05-04T19:47:52.327 に答える
1

DecimalFormatすでにリンクされている結果や回答を受け入れたくない場合は、カスタムソリューションを使用してここで処理するためのマルチステッププロセスがあると思います。

1)小数とグループ化の区切り文字を特定します。他の形式の記号(科学的記数法など)を識別する必要がある場合があります。

http://download.oracle.com/javase/1.4.2/docs/api/java/text/DecimalFormat.html#getDecimalFormatSymbols()

2)すべてのグループ化記号を削除します(または正規表現を作成します。受け入れる場合は、小数などの他の記号に注意してください)。次に、最初の小数点記号を取り除きます。必要に応じて他の記号。

3)parseまたはに電話しisNumberます。

于 2011-05-04T20:11:56.703 に答える
1

簡単なハックの1つは、取得した文字列に使用replaceFirstして、新しい文字列がdoubleかどうかを確認することです。ダブルの場合-逆変換(必要な場合)

于 2012-02-07T16:04:06.390 に答える
1

これは文字列を取り、その小数とコンマを数え、コンマを削除し、有効な小数を保存します(これは米国の標準化に基づいていることに注意してください-1.000.000,00を100万として処理するには、このプロセスは小数とカンマ処理が切り替えられました)、構造が有効かどうかを判断してから、doubleを返します。文字列を変換できなかった場合はnullを返します。編集:国際または米国のサポートが追加されました。米国の場合はconvertStoD(string、true)、米国以外の場合はconvertStoD(string、false)。コメントは米国版になりました。

public double convertStoD(string s,bool isUS){
 //string s = "some string or number, something dynamic";
 bool isNegative = false;
 if(s.charAt(0)== '-')
 {
  s = s.subString(1);
  isNegative = true;
 }
 string ValidNumberArguements = new string();
 if(isUS)
 {
   ValidNumberArguements = ",.";
 }else{
   ValidNumberArguements = ".,";
 }
 int length = s.length;
 int currentCommas = 0;
 int currentDecimals = 0;
 for(int i = 0; i < length; i++){
  if(s.charAt(i) == ValidNumberArguements.charAt(0))//charAt(0) = ,
  {
   currentCommas++;
   continue;
  }
  if(s.charAt(i) == ValidNumberArguements.charAt(1))//charAt(1) = .
  {
   currentDec++;
   continue;
  }
  if(s.charAt(i).matches("\D"))return null;//remove 1 A
 }
 if(currentDecimals > 1)return null;//remove 1.00.00
 string decimalValue = "";
 if(currentDecimals > 0)
 {
   int index = s.indexOf(ValidNumberArguements.charAt(1));
   decimalValue += s.substring(index);
   s = s.substring(0,index);
   if(decimalValue.indexOf(ValidNumberArguements.charAt(0)) != -1)return null;//remove 1.00,000
 }
 int allowedCommas = (s.length-1) / 3;
 if(currentCommas > allowedCommas)return null;//remove 10,00,000
 String[] NumberParser = s.split(ValidNumberArguements.charAt(0));
 length = NumberParser.length;
 StringBuilder returnString = new StringBuilder();
 for(int i = 0; i < length; i++)
 {
   if(i == 0)
   {
     if(NumberParser[i].length > 3 && length > 1)return null;//remove 1234,0,000
     returnString.append(NumberParser[i]);
     continue;
   }
   if(NumberParser[i].length != 3)return null;//ensure proper 1,000,000
   returnString.append(NumberParser[i]);
 }
 returnString.append(decimalValue);
 double answer = Double.parseDouble(returnString);
 if(isNegative)answer *= -1;
 return answer;
}
于 2012-02-10T23:02:24.893 に答える
-2

このコードは、すべての数字のグループが3であるIPアドレスを除いて、ほとんどの入力を処理する必要があります(例:255.255.255.255は有効ですが、255.1.255.255は有効ではありません)。また、科学的記数法もサポートしていません

セパレータのほとんどのバリエーション( "、"、 "。"またはスペース)で機能します。複数の区切り文字が検出された場合、最初の区切り文字は数千の区切り文字であると見なされ、追加のチェック(有効性など)が行われます。

編集: prevDigitは、数値が千の区切り記号を正しく使用していることを確認するために使用されます。数千のグループが複数ある場合は、最初のグループを除くすべてを3のグループにする必要があります。コードを変更して、「3」がマジックナンバーではなく定数になるように明確にしました。

編集2:私は反対票をあまり気にしませんが、誰かが問題が何であるかを説明できますか?

/* A number using thousand separator must have
   groups of 3 digits, except the first one.
   Numbers following the decimal separator can
   of course be unlimited. */
private final static int GROUP_SIZE=3;

public static boolean isNumber(String input) {
    boolean inThousandSep = false;
    boolean inDecimalSep = false;
    boolean endsWithDigit = false;
    char thousandSep = '\0';
    int prevDigits = 0;

    for(int i=0; i < input.length(); i++) {
        char c = input.charAt(i);

        switch(c) {
            case ',':
            case '.':
            case ' ':
                endsWithDigit = false;
                if(inDecimalSep)
                    return false;
                else if(inThousandSep) {
                    if(c != thousandSep)
                        inDecimalSep = true;
                    if(prevDigits != GROUP_SIZE)
                        return false; // Invalid use of separator
                }
                else {
                    if(prevDigits > GROUP_SIZE || prevDigits == 0)
                        return false;
                    thousandSep = c;
                    inThousandSep = true;
                }
                prevDigits = 0;
                break;

            default:
                if(Character.isDigit(c)) {
                    prevDigits++;
                    endsWithDigit = true;
                }
                else {
                    return false;
                }
        }
    }
    return endsWithDigit;
}

テストコード:

public static void main(String[] args) {
    System.out.println(isNumber("100"));               // true
    System.out.println(isNumber("100.00"));            // true
    System.out.println(isNumber("1,5"));               // true
    System.out.println(isNumber("1,000,000.00."));     // false
    System.out.println(isNumber("100,00,2"));          // false
    System.out.println(isNumber("123.123.23.123"));    // false
    System.out.println(isNumber("123.123.123.123"));   // true       
}
于 2012-02-10T22:11:50.693 に答える