确定字符串是否为数字并在Java中转换?

确定字符串是否为数字并在Java中转换?,java,numbers,bigdecimal,string-parsing,Java,Numbers,Bigdecimal,String Parsing,我知道这个问题的变体以前经常被问到(例如,请参见和),但这是而不是一个精确的重复 我想检查字符串是否是数字,如果是,我想将其存储为双精度。有几种方法可以做到这一点,但它们似乎都不适合我的目的 一种解决方案是使用Double.parseDouble或类似的新的BigDecimal。但是,如果存在逗号,则这些解决方案不起作用(因此“1234”将导致异常)。在使用这些技术之前,我当然可以去掉所有逗号,但这在其他地区似乎会带来很多问题 我查看了ApacheCommonsNumberUtils.isNum

我知道这个问题的变体以前经常被问到(例如,请参见和),但这是而不是一个精确的重复

我想检查
字符串
是否是数字,如果是,我想将其存储为
双精度
。有几种方法可以做到这一点,但它们似乎都不适合我的目的

一种解决方案是使用
Double.parseDouble
或类似的
新的BigDecimal
。但是,如果存在逗号,则这些解决方案不起作用(因此“1234”将导致异常)。在使用这些技术之前,我当然可以去掉所有逗号,但这在其他地区似乎会带来很多问题

我查看了ApacheCommons
NumberUtils.isNumber
,但它也存在相同的逗号问题

我考虑过
NumberFormat
DecimalFormat
,但这些似乎太宽大了。例如,“1A”被格式化为“1”,而不是表示它不是一个数字。此外,类似“127.0.0.1”的内容将被计算为数字127,而不是表示它不是一个数字


我觉得我的需求并不是第一个这么做的,但没有一个解决方案能完全满足我的需求。我想甚至我也不知道我需要什么(否则我可以编写自己的解析器),但是我知道上述解决方案不起作用,原因如下。是否存在任何解决方案,或者我是否需要准确了解我需要什么,并为其编写自己的代码?

如果您设置了正确的区域设置,内置的
parseDouble
将使用逗号。例如。

如果您不愿意接受DecimalFormat的结果或已链接的答案,我认为您需要使用自定义解决方案处理多步骤流程

1) 识别小数和分组分隔符。您可能需要识别其他格式符号(如科学符号指示器)

()

2) 去掉所有分组符号(或者创建一个正则表达式,如果接受,请小心其他符号,如十进制)。然后去掉第一个十进制符号。其他需要的符号


3) 调用
parse
isNumber

您可以指定所需的区域设置:

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

这应该在您的示例中起作用,因为德语区域设置中有逗号作为十进制分隔符。

您可以使用ParsePosition检查NumberFormat.parse操作中字符串的完整使用情况。如果字符串已被使用,则不会出现“1A”情况。如果没有,你可以这样做,也可以这样做。请参阅,了解解决方案的简要说明以及由于ParsePosition选项而无法修复的相关JDK错误。

一个简单的破解方法是对您获得的字符串使用
replaceFirst
,并检查新字符串是否为双精度字符串。如果是双转换回(如果需要的话)

听起来很奇怪,但我会尝试遵循并使用
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");
对于输入,如
1A
127.0.0.1
1234
6.02e-23
,我得到以下输出:

Not a number
Not a number
1234
6.02E-23

Scanner.useLocale
可用于更改为所需的区域设置。

不确定它是否满足您的所有要求,但找到的代码可能会为您指明正确的方向

从文章中:

总之,正确输入处理的步骤如下:

  • 获取适当的NumberFormat并定义ParsePosition变量
  • 将ParsePosition索引设置为零
  • 使用Parse(字符串源、ParsePosition、ParsePosition)解析输入值
  • 如果输入长度和ParsePosition索引值不匹配,或者如果解析的数字为空,则执行错误操作
  • 否则,该值通过验证

  • 如果要将逗号分隔的十进制字符串数转换为双精度,可以使用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
    

    不幸的是,Double.parseDouble或新的BigDecimal似乎是您最好的选择

    您提到了本地化方面的问题,但不幸的是,无论如何都无法可靠地支持用户不使用规范的所有区域设置。这是不可能的

    有时,您可以通过查看是否首先使用逗号或句点(如果两者都使用)来推断所使用的方案,但这并不总是可能的,所以为什么还要尝试呢?最好有一个系统,你知道它在某些情况下工作可靠,而不是试图依赖一个可能在更多情况下工作,但也可能产生不良结果

    数字123456代表什么?123456还是123.456

    根据用户指定的语言环境,只需去掉逗号、空格或句点。默认为剥离空格和逗号。如果要使其更严格,请仅使用带逗号或空格,而不能同时使用这两个逗号或空格,如果有逗号或空格,则仅在句点之前使用。也应该很容易手动检查,如果他们是正确的间隔在三个。事实上,在这里定制解析器可能是最简单的

    这里有一点概念证明。这有点(非常)混乱,但我认为它是有效的,而且你无论如何都能理解:)

    公共类StrictNumberParser{
    公共双解析(String numberString)抛出NumberFormatException{
    numberString=numberString.trim();
    char[]numbercars=numberString.toCharArray();
    字符分隔符=null;
    int-separatorCount=0;
    布尔noMoreSeparators=false;
    for(int index=1;indexpublic 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");
      }
    }
    
    /* 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       
    }
    
    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;
    }
    
    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;
        }
    } 
    
    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;
    }
    
    }
    
       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);
       }
       }