37

既然 Java 没有可空类型,也没有 TryParse(),那么如何在不引发异常的情况下处理输入验证?

通常的方式:

String userdata = /*value from gui*/
int val;
try
{
   val = Integer.parseInt(userdata);
}
catch (NumberFormatException nfe)
{
   // bad data - set to sentinel
   val = Integer.MIN_VALUE;
}

我可以使用正则表达式来检查它是否可解析,但这似乎也有很多开销。

处理这种情况的最佳做法是什么?

编辑:理由:关于异常处理的讨论很多,一般的态度是异常应该只用于意外情况。但是,我认为糟糕的用户输入是意料之中的,并不罕见。是的,这确实是一个学术观点。

进一步编辑:

一些答案确切地说明了 SO 有什么问题。你忽略了被问到的问题,并回答了另一个与它无关的问题。问题不是询问层之间的过渡。如果数字不可解析,问题不是要返回什么。众所周知,val = Integer.MIN_VALUE; 对于这个完全无上下文的代码片段的应用程序来说,这正是正确的选择。

4

16 回答 16

28

我问是否有开源实用程序库可以为您进行解析,答案是肯定的!

Apache Commons Lang你可以使用NumberUtils.toInt

// returns defaultValue if the string cannot be parsed.
int i = org.apache.commons.lang.math.NumberUtils.toInt(s, defaultValue);

谷歌番石榴你可以使用Ints.tryParse

// returns null if the string cannot be parsed
// Will throw a NullPointerException if the string is null
Integer i = com.google.common.primitives.Ints.tryParse(s);

无需编写自己的方法来解析数字而不会引发异常。

于 2013-05-22T18:24:34.060 回答
17

对于用户提供的数据, Integer.parseInt 通常是错误的方法,因为它不支持国际化。该java.text软件包是您的(详细)朋友。

try {
    NumberFormat format = NumberFormat.getIntegerInstance(locale);
    format.setParseIntegerOnly(true);
    format.setMaximumIntegerDigits(9);
    ParsePosition pos = new ParsePosition(0);
    int val = format.parse(str, pos).intValue();
    if (pos.getIndex() != str.length()) {
        // ... handle case of extraneous characters after digits ...
    }
    // ... use val ...
} catch (java.text.ParseFormatException exc) {
    // ... handle this case appropriately ...
}
于 2008-10-06T15:05:00.090 回答
16

差不多就是这样,尽管返回 MIN_VALUE 有点问题,除非您确定将其用于您本质上用作错误代码的内容是正确的。不过,至少我会记录错误代码行为。

可能也有用(取决于应用程序)记录错误输入,以便您可以跟踪。

于 2008-10-06T14:32:07.723 回答
11

你的方法有什么问题?我不认为这样做会损害您的应用程序的性能。这是正确的做法。不要过早优化

于 2008-10-06T14:32:22.387 回答
6

我确定它是错误的形式,但是我在 Utilities 类上有一组静态方法,这些方法可以执行以下操作:Utilities.tryParseInt(String value)如果 String 不可解析,则返回 0,如果抛出异常Utilities.tryParseInt(String value, int defaultValue),则允许您指定要使用的值。parseInt()

我相信有时候返回一个错误输入的已知值是完全可以接受的。一个非常人为的例子:您向用户询问格式为 YYYYMMDD 的日期,他们给了您错误的输入。做类似Utilities.tryParseInt(date, 19000101)Utilities.tryParseInt(date, 29991231);取决于程序要求的事情可能是完全可以接受的。

于 2008-10-06T14:38:13.920 回答
3

我将重申 stinkyminky 在帖子底部提出的观点:

一种普遍接受的验证用户输入(或来自配置文件等的输入)的方法是在实际处理数据之前使用验证。在大多数情况下,这是一个很好的设计举措,即使它可能导致对解析算法的多次调用。

一旦您知道您已经正确验证了用户输入,那么就可以安全地对其进行解析并忽略、记录或转换为 RuntimeException NumberFormatException。

请注意,这种方法需要您从两部分考虑您的模型:业务模型(我们真正关心的是 int 或 float 格式的值)和用户界面模型(我们真正希望允许用户输入任何内容的地方)想)。

为了使数据从用户界面模型迁移到业务模型,它必须通过一个验证步骤(这可以逐个字段发生,但大多数场景需要对正在配置的整个对象进行验证) .

如果验证失败,则会向用户提供反馈,告知他们做错了什么,并有机会修复它。

像 JGoodies Binding 和 JSR 295 这样的绑定库使这种事情比听起来更容易实现 - 许多 Web 框架提供将用户输入与实际业务模型分开的构造,仅在验证完成后填充业务对象。

就配置文件的验证(某些评论中提出的另一个用例)而言,如果根本没有指定特定值,则指定默认值是一回事 - 但如果数据格式错误(有人键入 '哦'而不是'零' - 或者他们从 MS Word 复制并且所有反引号都有一个时髦的 unicode 字符),然后需要某种系统反馈(即使它只是通过抛出运行时异常而使应用程序失败) .

于 2008-10-07T03:54:15.233 回答
2

这是我的做法:

public Integer parseInt(String data) {
  Integer val = null;
  try {
    val = Integer.parseInt(userdata);
  } catch (NumberFormatException nfe) { }
  return val;
}

然后 null 表示无效数据。如果您想要一个默认值,您可以将其更改为:

public Integer parseInt(String data,int default) {
  Integer val = default;
  try {
    val = Integer.parseInt(userdata);
  } catch (NumberFormatException nfe) { }
  return val;
}
于 2008-10-06T15:13:30.900 回答
1

我认为最佳实践是您显示的代码。

由于开销,我不会选择正则表达式替代品。

于 2008-10-06T14:32:16.210 回答
1

试试org.apache.commons.lang.math.NumberUtils.createInteger(String s)。这对我帮助很大。对于双打,多头等也有类似的方法。

于 2012-12-05T15:23:22.110 回答
0

您可以使用整数,如果您的值不正确,可以将其设置为 null。如果您使用的是 java 1.6,它将为您提供自动装箱/拆箱。

于 2008-10-06T14:41:21.683 回答
0

更清晰的语义(Java 8 OptionalInt)

对于 Java 8+,我可能会使用 RegEx 进行预过滤(以避免您提到的异常),然后将结果包装在一个原始可选中(以处理“默认”问题):

public static OptionalInt toInt(final String input) {
    return input.matches("[+-]?\\d+") 
            ? OptionalInt.of(Integer.parseInt(input)) 
            : OptionalInt.empty();
}

如果您有许多 String 输入,您可能会考虑返回 anIntStream而不是,OptionalInt以便您可以flatMap().

参考

于 2017-07-11T08:03:08.487 回答
-1

上面的代码很糟糕,因为它与以下代码等价。

// this is bad
int val = Integer.MIN_VALUE;
try
{
   val = Integer.parseInt(userdata);
}
catch (NumberFormatException ignoreException) { }

该异常被完全忽略。此外,魔术令牌很糟糕,因为用户可以传入 -2147483648 (Integer.MIN_VALUE)。

通用的可解析问题没有好处。相反,它应该与上下文相关。您的应用程序有特定要求。您可以将您的方法定义为

private boolean isUserValueAcceptable(String userData)
{
   return (    isNumber(userData)    
          &&   isInteger(userData)   
          &&   isBetween(userData, Integer.MIN_VALUE, Integer.MAX_VALUE ) 
          );
}

您可以在哪里记录需求,并且可以创建定义良好且可测试的规则。

于 2008-10-06T15:01:08.867 回答
-1

如果您可以像您所说的(isParsable())那样通过预先测试来避免异常,那可能会更好——但并非所有库的设计都考虑到了这一点。

我用了你的把戏,这很糟糕,因为无论你是否捕捉到它们,我的嵌入式系统上的堆栈跟踪都会被打印出来:(

于 2008-10-06T16:04:21.670 回答
-2

异常机制很有价值,因为它是获取状态指示符和响应值的唯一方法。此外,状态指示器是标准化的。如果有错误,你会得到一个异常。这样您就不必自己考虑错误指示器。争议不在于异常,而在于检查的异常(例如,您必须捕获或声明的异常)。

就个人而言,我觉得您选择了异常非常有价值的示例之一。用户输入错误值是一个常见问题,通常您需要返回给用户以获取正确值。如果您询问用户,您通常不会恢复为默认值;这给用户的印象是他的输入很重要。

如果您不想处理异常,只需将其包装在 RuntimeException(或派生类)中,它将允许您忽略代码中的异常(并在发生时终止您的应用程序;有时也可以)。

关于如何处理 NumberFormat 异常的一些示例: 在 Web 应用程序配置数据中:

loadCertainProperty(String propVal) {
  try
  {
    val = Integer.parseInt(userdata);
    return val;
  }
  catch (NumberFormatException nfe)
  { // RuntimeException need not be declared
    throw new RuntimeException("Property certainProperty in your configuration is expected to be " +
                               " an integer, but was '" + propVal + "'. Please correct your " +
                               "configuration and start again");
    // After starting an enterprise application the sysadmin should always check availability
    // and can now correct the property value
  }
}

在 GUI 中:

public int askValue() {
  // TODO add opt-out button; see Swing docs for standard dialog handling
  boolean valueOk = false;
  while(!valueOk) {
    try {
      String val = dialog("Please enter integer value for FOO");
      val = Integer.parseInt(userdata);
      return val; 
    } catch (NumberFormatException nfe) {
      // Ignoring this; I don't care how many typo's the customer makes
    }
  }
}

在 Web 表单中:将表单返回给用户,并提供有用的错误消息和更正的机会。大多数框架都提供了一种标准化的验证方式。

于 2008-10-06T18:12:02.507 回答
-2

Integer.MIN_VALUE 作为 NumberFormatException 是个坏主意。

您可以将提案添加到 Project Coin 以将此方法添加到 Integer

@Nullable public static Integer parseInteger (String src)...它将返回 null 输入错误

然后将您的提案的链接放在这里,我们都会投票赞成!

PS:看看这个 http://msdn.microsoft.com/en-us/library/bb397679.aspx 这可能是多么丑陋和臃肿

于 2010-12-03T12:18:07.427 回答
-5

在它前面放一些 if 语句。如果(空!=用户数据)

于 2008-10-06T14:31:44.963 回答