Determine if a String is a number and convert in J

2020-05-17 04:52发布

I know variants of this question have been asked frequently before (see here and here for instance), but this is not an exact duplicate of those.

I would like to check if a String is a number, and if so I would like to store it as a double. There are several ways to do this, but all of them seem inappropriate for my purposes.

One solution would be to use Double.parseDouble(s) or similarly new BigDecimal(s). However, those solutions don't work if there are commas present (so "1,234" would cause an exception). I could of course strip out all commas before using these techniques, but that would seem to pose loads of problems in other locales.

I looked at Apache Commons NumberUtils.isNumber(s), but that suffers from the same comma issue.

I considered NumberFormat or DecimalFormat, but those seemed far too lenient. For instance, "1A" is formatted to "1" instead of indicating that it's not a number. Furthermore, something like "127.0.0.1" will be counted as the number 127 instead of indicating that it's not a number.

I feel like my requirements aren't so exotic that I'm the first to do this, but none of the solutions does exactly what I need. I suppose even I don't know exactly what I need (otherwise I could write my own parser), but I know the above solutions do not work for the reasons indicated. Does any solution exist, or do I need to figure out precisely what I need and write my own code for it?

15条回答
Anthone
2楼-- · 2020-05-17 05:34

Sounds quite weird, but I would try to follow this answer and use 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");

For inputs such as 1A, 127.0.0.1, 1,234, 6.02e-23 I get the following output:

Not a number
Not a number
1234
6.02E-23

Scanner.useLocale can be used to change to the desired locale.

查看更多
成全新的幸福
3楼-- · 2020-05-17 05:34

This is an interesting problem. But perhaps it is a little open-ended? Are you looking specifically to identify base-10 numbers, or hex, or what? I'm assuming base-10. What about currency? Is that important? Or is it just numbers.

In any case, I think that you can use the deficiencies of Number format to your advantage. Since you no that something like "1A", will be interpreted as 1, why not check the result by formatting it and comparing against the original string?

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

What do you think?

查看更多
甜甜的少女心
4楼-- · 2020-05-17 05:38

I think you've got a multi step process to handle here with a custom solution, if you're not willing to accept the results of DecimalFormat or the answers already linked.

1) Identify the decimal and grouping separators. You might need to identify other format symbols (such as scientific notation indicators).

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

2) Strip out all grouping symbols (or craft a regex, be careful of other symbols you accept such as the decimal if you do). Then strip out the first decimal symbol. Other symbols as needed.

3) Call parse or isNumber.

查看更多
▲ chillily
5楼-- · 2020-05-17 05:41

This is really interesting, and I think people are trying to overcomplicate it. I would really just break this down by rules:

1) Check for scientific notation (does it match the pattern of being all numbers, commas, periods, -/+ and having an 'e' in it?) -- if so, parse however you want

2) Does it match the regexp for valid numeric characters (0-9 , . - +) (only 1 . - or + allowed) if so, strip out everything that's not a digit and parse appropriately, otherwise fail.

I can't see a shortcut that's going to work here, just take the brute force approach, not everything in programming can be (or needs to be) completely elegant.

查看更多
姐就是有狂的资本
6楼-- · 2020-05-17 05:41

If you set your locale right, built in parseDouble will work with commas. Example is here.

查看更多
神经病院院长
7楼-- · 2020-05-17 05:48

You're best off doing it manually. Figure out what you can accept as a number and disregard everything else:

   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);
   }
   }
查看更多
登录 后发表回答