如何处理具有相同 catch 块的解析数字?

How to handle parsing numbers with same catch block?

我经常遇到需要解析数值(例如使用 Integer.parseInt 或 Double.parseDouble)并且我有多个值的情况。问题是我发现自己不得不重复异常处理,它变得很难看。例如,采用以下代码:

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

有没有好的模式来处理这种情况?

我不想使用单次捕获,因为我想继续解析其余数字。

我应该提一下,在这个例子中,值没有被初始化,但在实际的程序代码中它们会被初始化。仅当字符串值可解析时才应进行赋值。

我只想对您的所有值使用一个 try-catch。

只是提取一个方法:

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

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

这是一个使用标志和循环来防止异常再次发生的解决方案:

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

Double 的文档中,您可以找到问题的解决方案。

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

将所有内容包装在 parseDouble 方法中并按照说明进行操作

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

从你的问题来看,它似乎被另一条评论所取代

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

如果 link 变为无效(永远不会发生),这就是正则表达式

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"

您可以像这样实现 class:

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

}

然后像这样使用它:

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

当您预期解析失败时,我发现根本不使用基于异常的方法更简单。除了导致更简洁的代码之外,它还可以快几个数量级,因为它避免了抛出异常的代价。

与其编写我自己的方法,不如像往常一样使用 Guava 进行救援。您可以像这样用 Doubles.tryParse 重写您的解析:

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

非常简洁!请注意,在此执行后,任何无法解析的值都将是 null。如果解析失败,您实际上还没有说出要分配给 Doubles 的值(实际上您的原始示例将无法编译,因为这些值可能未初始化)。

假设您想要将值 0.0 分配给任何失败的解析 - 您可以使用 Objects.firstNonNull() 来做到这一点:

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

我决定使用这个方法:

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

然后可以这样赋值:

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