Sina Madani
Sina Madani

Reputation: 1324

How to handle parsing numbers with same catch block?

I come across a situation quite often where I need to parse a numerical value (e.g. with Integer.parseInt or Double.parseDouble) and I have multiple values. The problem is that I find myself having to duplicate the exception-handling and it becomes ugly. For instance, take the following code:

double lowVal, mediumVal, highVal;
String lowString = "1.2", mediumString = "null", highString = "7.9";

try {
    lowVal = parseDouble(lowString);
} catch (NumberFormatException NaN) {
    //Don't care, just carry on trying to parse the rest...
}

try {
    mediumVal = parseDouble(mediumString);
} catch (NumberFormatException NaN) {
    //Don't care, just carry on trying to parse the rest...
}

try {
    highVal = parseDouble(highString);
} catch (NumberFormatException NaN) {
    //Don't care, just carry on trying to parse the rest...
}

Is there a good pattern for dealing with this situation?

I don't want to use a single-try catch because I want to continue parsing the rest of the numbers.

I should mention that in this example, the values are not initialized but in actual program code they would be. The assignment should only occur if the string values are parseable.

Upvotes: 3

Views: 1916

Answers (7)

Krzysztof Cichocki
Krzysztof Cichocki

Reputation: 6414

just extract a method:

double lowVal, mediumVal, highVal;
String lowString = "1.2", mediumString = "null", highString = "7.9";

lowVal = parseDouble(lowString);
mediumVal = parseDouble(mediumString);
highVal = parseDouble(highString);

double parseDouble(String s) {
    try {
        return Double.parseDouble(s);
    } catch (NumberFormatException e) {
        return Double.NAN;
    } 
}

or

Double lowVal;
Double mediumVal;
Double highVal;
String lowString = "1.2", mediumString = "null", highString = "7.9";

lowVal = parseDouble(lowString);
mediumVal = parseDouble(mediumString);
highVal = parseDouble(highString);

Double parseDouble(String s) {
    try {
        return Double.parseDouble(s);
    } catch (NumberFormatException e) {
        return null;
    } 
}

Upvotes: 4

Sina Madani
Sina Madani

Reputation: 1324

I've decided to use this method:

public static double parseWithDefault(String value, double fallback) {
    try {
        return Double.parseDouble(value);
    } catch (NumberFormatException NaN) {
        return fallback;
    }
}

And then can make assignments like so:

lowVal = parseWithDefault(lowString, lowVal);
mediumVal = parseWithDefault(mediumString, mediumVal);
highVal = parseWithDefault(highString, highVal);

Upvotes: 0

BeeOnRope
BeeOnRope

Reputation: 65006

When you expect parsing failures, I find it simpler not to use the exception based methods at all. In addition to leading to more concise code, it can be several orders of magnitude faster since it avoids the expense of throwing exceptions.

Rather than writing my own methods, it's Guava to the rescue as usual. You can rewrite your parsing with Doubles.tryParse like so:

Double lowVal, mediumVal, highVal;
String lowString = "1.2", mediumString = "null", highString = "7.9";    

lowVal = Doubles.tryParse(lowString);  
mediumVal = Doubles.tryParse(mediumString);
highVal = Doubles.tryParse(highString);

Very concise! Note that any values that couldn't be parsed will be null after this executes. You haven't actually said what value you want to assign to the Doubles if the parsing fails (and in fact your original example won't compile since the values are potentially uninitialized).

Let's assume you wanted to assign the value 0.0 to any failed parsing - you could use Objects.firstNonNull() to do that:

Double lowVal, mediumVal, highVal;
String lowString = "1.2", mediumString = "null", highString = "7.9";    

lowVal = Objects.firstNonNull(Doubles.tryParse(lowString), 0.0);  
mediumVal = Objects.firstNonNull(Doubles.tryParse(mediumString), 0.0);
highVal = Objects.firstNonNull(Doubles.tryParse(highString), 0.0);

Upvotes: 0

Radu Ionescu
Radu Ionescu

Reputation: 3532

In the docs of Double you have the solution to your problem.

To avoid calling this method on an invalid string and having a NumberFormatException be thrown, the regular expression below can be used to screen the input string

Wrap everything in your parseDouble method and follow the instructions

if (Pattern.matches(fpRegex, myString))
            Double.valueOf(myString); // Will not throw NumberFormatException
        else {
            // Perform suitable alternative action
        }

Which from your question seems to be replacing it by another comment

//Don't care, just carry on trying to parse the rest...

In case the link becomes innactive (should never happen) this is the regex

final String Digits     = "(\\p{Digit}+)";
final String HexDigits  = "(\\p{XDigit}+)";
    // an exponent is 'e' or 'E' followed by an optionally 
    // signed decimal integer.
    final String Exp        = "[eE][+-]?"+Digits;
    final String fpRegex    =
        ("[\\x00-\\x20]*"+  // Optional leading "whitespace"
         "[+-]?(" + // Optional sign character
         "NaN|" +           // "NaN" string
         "Infinity|" +      // "Infinity" string

         // A decimal floating-point string representing a finite positive
         // number without a leading sign has at most five basic pieces:
         // Digits . Digits ExponentPart FloatTypeSuffix
         // 
         // Since this method allows integer-only strings as input
         // in addition to strings of floating-point literals, the
         // two sub-patterns below are simplifications of the grammar
         // productions from the Java Language Specification, 2nd 
         // edition, section 3.10.2.

         // Digits ._opt Digits_opt ExponentPart_opt FloatTypeSuffix_opt
         "((("+Digits+"(\\.)?("+Digits+"?)("+Exp+")?)|"+

         // . Digits ExponentPart_opt FloatTypeSuffix_opt
         "(\\.("+Digits+")("+Exp+")?)|"+

   // Hexadecimal strings
   "((" +
    // 0[xX] HexDigits ._opt BinaryExponent FloatTypeSuffix_opt
    "(0[xX]" + HexDigits + "(\\.)?)|" +

    // 0[xX] HexDigits_opt . HexDigits BinaryExponent FloatTypeSuffix_opt
    "(0[xX]" + HexDigits + "?(\\.)" + HexDigits + ")" +

    ")[pP][+-]?" + Digits + "))" +
         "[fFdD]?))" +
         "[\\x00-\\x20]*");// Optional trailing "whitespace"

Upvotes: 1

StealthSpoder
StealthSpoder

Reputation: 346

Here is a solution using flags and a loop to prevent the exceptions from occurring again:

    double lowVal, mediumVal, highVal;
    String lowString = "1.2", mediumString = "null", highString = "7.9";
    int count = 0;
    boolean lowFlag = false, medFlag = false, highFlag = false;
    do{
        try {
            count = 0;
            count++;
            if(!lowFlag)
                lowVal = parseDouble(lowString);
            count++;
            if(!medFlag)
                mediumVal = parseDouble(mediumString);
            count++;
            if(!highFlag)
                highVal = parseDouble(highString);

            break;

        } catch (NumberFormatException NaN) {
            if(count==0)
                lowFlag = true;
            else if(count==1)
                medFlag = true;
            else if(count==2)
                highFlag = true;
        }
    }while(true);

Upvotes: 0

stjepano
stjepano

Reputation: 1162

You can implement a class like this:

class DoubleParser {

    private Optional<Double> parsedOptional;

    private DoubleParser(Optional<Double> parsedOptional) {
        this.parsedOptional = parsedOptional;
    }

    public static DoubleParser parse(final String s) {
        Double parsed = null;
        try {
            parsed = Double.valueOf(s);
        } catch ( NumberFormatException e ) {
            parsed = null;
        }

        return new DoubleParser(Optional.ofNullable(parsed));
    }

    public double get() {
        return get(0.0);
    }

    public double get(final double defaultValue) {
        return parsedOptional.orElse(defaultValue);
    }

}

And then use it like this:

double lowVal, mediumVal, highVal;
String lowString = "1.2", mediumString = "null", highString = "7.9";

lowVal = DoubleParser.parse(lowString).get();
mediumVal = DoubleParser.parse(mediumString).get();
highVal = DoubleParser.parse(highString).get();

// with default value if you want
mediumVal = DoubleParser.parse(mediumString).get(Double.NaN);

Upvotes: 1

slothdotpy
slothdotpy

Reputation: 94

I would just use a single try-catch for all your values.

Upvotes: 0

Related Questions