5

我们都知道大量抛出的异常会对我们的应用程序的性能产生影响,因此,我们应该远离诸如使用异常进行控制流之类的事情。在此声明之后,我必须承认,在编码时我并不太关心这一点。我主要在 Java 平台上工作,但最近我在 .NET 平台上做,发现了这个方便的方法:public static bool TryParse(string s,out int result) ,它允许你将 String 转换为 int 而不会引发异常。从那一刻起,我一直在使用它。我只是想问你关于使用public static bool TryParse(string s,out int result)or的偏好public static int ToInt32(string value)

从 Java 的角度来看,只是指出它缺少这样一个类似的方法,尽管我们可以通过以下方式获得它:

boolean isInteger = Pattern.matches("^\d*$", myString);

谢谢。

4

5 回答 5

7

是的,Java 缺少一个类似的方法,尽管没有out参数它实际上很难表达(同时想要返回一个原语)。但是,通常,在 C# 中,TryParse如果您希望值有时不是整数,则应使用,ToInt32否则;这样,“异常”情况就被这样对待了。

特别是如果性能是您想要的主要原因TryParse,那么您发布的正则表达式匹配方法要差得多。异常的性能“开销”(实际上是非常小的)与错误地使用它们的程度相形见绌,这会模糊对控制流的简单理解。

于 2009-05-27T10:39:53.723 回答
3

我不了解 C#,但在 Java 中,异常只有在实际抛出时才会昂贵,但它们确实非常昂贵。如果您预计大部分字符串无效,那么首先验证它们是值得的,即使您使用正则表达式也是如此。

但不要使用String.matches()Pattern.matches()应用正则表达式;每次调用这些方法时,它们都会重新编译正则表达式。相反,提前编译正则表达式并将其保存为 Pattern 对象,然后使用它进行验证。Integer.parseInt()在我的测试中,解析 10,000 个字符串列表,其中 20% 无效,使用 Pattern 进行预验证几乎是单独使用并捕获异常的两倍。

但是,此讨论仅适用于您在紧密循环中进行大量转换的情况。如果你只是偶尔做一次,比如当你接受用户输入时,让Integer.parseInt()验证就可以了。而且,如果您确实选择使用正则表达式进行验证,那么您将需要一个比它更好的正则表达式^\d*$- 该正则表达式将匹配空字符串以及大于 的“数字” Integer.MAX_VALUE,并且它根本不会匹配负数。

于 2009-05-27T12:35:19.170 回答
1

为此,您可以在 Java 中使用众所周知的 StringUtils(在 commons-lang 上),此类有一个方法isNumeric

也许你可以看看那些人为那个函数写的代码:

public static boolean isNumeric(String str) {
  if (str == null) {
    return false;
  }
  int sz = str.length();
  for (int i = 0; i < sz; i++) {
    if (Character.isDigit(str.charAt(i)) == false) {
      return false;
    }
  }
  return true;
 }

我并不是说这是最有效的方法,但您还有另一种选择,无需使用正则表达式。祝你好运!

于 2009-05-27T11:03:40.353 回答
1

从 Java 的角度来看,只是指出它缺少这样一个类似的方法,尽管我们可以通过以下方式获得它:

boolean isInteger = Pattern.matches("^\d*$", myString);

要预测是否Integer.parseInt(myString)会抛出异常,还有更多工作要做。字符串可以以-. 此外,一个 int 的有效数字不能超过 10 个。所以更可靠的表达式是^-?0*\d{1,10}$. 但是即使这个表达式也不能预测每个异常,因为它仍然太不精确。

生成可靠的正则表达式是可能的。但这会很长。也可以实现一个精确确定 parseInt 是否会抛出异常的方法。它可能看起来像这样:

static boolean wouldParseIntThrowException(String s) {
    if (s == null || s.length() == 0) {
        return true;
    }

    char[] max = Integer.toString(Integer.MAX_VALUE).toCharArray();
    int i = 0, j = 0, len = s.length();
    boolean maybeOutOfBounds = true;

    if (s.charAt(0) == '-') {
        if (len == 1) {
            return true; // s == "-"
        }
        i = 1;
        max[max.length - 1]++; // 2147483647 -> 2147483648
    }
    while (i < len && s.charAt(i) == '0') {
        i++;
    }
    if (max.length < len - i) {
        return true; // too long / out of bounds
    } else if (len - i < max.length) {
        maybeOutOfBounds = false;
    }
    while (i < len) {
        char digit = s.charAt(i++);
        if (digit < '0' || '9' < digit) {
            return true;
        } else if (maybeOutOfBounds) {
            char maxdigit = max[j++];
            if (maxdigit < digit) {
                return true; // out of bounds
            } else if (digit < maxdigit) {
                maybeOutOfBounds = false;
            }
        }
    }
    return false;
}

我不知道哪个版本更有效。这主要取决于上下文哪种检查是合理的。

在 C# 中要检查是否可以转换字符串,您将使用 TryParse。如果它返回 true,那么它作为副产品同时转换。这是一个简洁的功能,我认为仅重新实现 parseInt 以返回 null 而不是抛出异常没有问题。

但是,如果您不想重新实现解析方法,那么手头有一组可以根据情况使用的方法仍然会很好。它们可能看起来像这样:

private static Pattern QUITE_ACCURATE_INT_PATTERN = Pattern.compile("^-?0*\\d{1,10}$");

static Integer tryParseIntegerWhichProbablyResultsInOverflow(String s) {
    Integer result = null;
    if (!wouldParseIntThrowException(s)) {
        try {
            result = Integer.parseInt(s);
        } catch (NumberFormatException ignored) {
            // never happens
        }
    }
    return result;
}

static Integer tryParseIntegerWhichIsMostLikelyNotEvenNumeric(String s) {
    Integer result = null;
    if (s != null && s.length() > 0 && QUITE_ACCURATE_INT_PATTERN.matcher(s).find()) {
        try {
            result = Integer.parseInt(s);
        } catch (NumberFormatException ignored) {
        // only happens if the number is too big
        }
    }
    return result;
}

static Integer tryParseInteger(String s) {
    Integer result = null;
    if (s != null && s.length() > 0) {
        try {
            result = Integer.parseInt(s);
        } catch (NumberFormatException ignored) {
        }
    }
    return result;
}

static Integer tryParseIntegerWithoutAnyChecks(String s) {
    try {
        return Integer.parseInt(s);
    } catch (NumberFormatException ignored) {
    }
    return null;
}
于 2010-12-11T15:03:16.783 回答
0

我只是想问你关于使用 public static bool TryParse(string s,out int result) 或 public static int ToInt32(string value) 的偏好。

是的,我使用 TryParse,除非我希望该值始终有效。我发现它比使用异常更清晰。即使我想要异常,我通常也想自定义消息或抛出我自己的自定义异常;因此,我使用 TryParse 并手动抛出异常。

在 Java 和 C# 中,我尝试捕获尽可能的异常集。在 Java 中,这意味着我必须分别捕获 NullPointerException 和 NumberFormatException 以响应 Number.ValueOf(...); 或者,我可以捕获“异常”并冒着捕获意外的风险。使用 C# 中的 TryParse,我完全不用担心。

于 2009-05-27T16:48:40.703 回答