java中的字符串到字符串 - 可能是坏数据,需要避免异常

时间:2008-10-06 14:29:23

标签: java parsing numbers

看到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的讨论,一般的态度是异常应仅用于意外情况。但是,我认为糟糕的用户输入是预期的,并不罕见。是的,这确实是一个学术观点。

进一步编辑:

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

16 个答案:

答案 0 :(得分:27)

我问if there were open source utility libraries that had methods to do this parsing for you,答案是肯定的!

Apache Commons Lang开始,您可以使用NumberUtils.toInt

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

Google Guava开始,您可以使用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);

没有必要编写自己的方法来解析数字而不会抛出异常。

答案 1 :(得分: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 ...
}

答案 2 :(得分:16)

这就是它,尽管返回MIN_VALUE有点值得怀疑,除非你确定使用你正在使用的错误代码是正确的。但至少我会记录错误代码的行为。

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

答案 3 :(得分:11)

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

答案 4 :(得分:6)

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

我相信有时候输入错误输入的已知值是完全可以接受的。一个非常人为的例子:你要求用户提供YYYYMMDD格式的日期,他们会给你不好的输入。根据计划要求,执行Utilities.tryParseInt(date, 19000101)Utilities.tryParseInt(date, 29991231);等操作可能是完全可以接受的。

答案 5 :(得分:3)

我要重申stinkyminky正在向帖子底部发出的观点:

验证用户输入(或来自配置文件等的输入......)的一般公认的方法是在实际处理数据之前使用验证。在大多数的情况下,这是一个很好的设计动作,即使它可能导致多次调用解析算法。

一旦您知道您已正确验证用户输入,然后就可以安全地解析它并忽略,记录或转换为RuntimeException NumberFormatException。

请注意,这种方法要求您将模型分为两部分:业务模型(我们实际关心的是int或float格式的值)和用户界面模型(我们真正希望允许用户放置的位置)无论他们想要什么。)

为了使数据从用户界面模型迁移到业务模型,它必须通过验证步骤(这可以在逐个字段的基础上进行,但是大多数情况都要求对整个对象进行验证。正在配置)。

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

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

在验证配置文件(某些注释中提供的其他用例)方面,如果根本没有指定特定值,则指定默认值是一回事 - 但如果数据格式错误(某人)输入'oh'而不是'zero' - 或者他们从MS Word中复制并且所有后面的滴答声都有一个时髦的unicode字符),然后需要某种系统反馈(即使它只是通过投掷一个失败的应用程序运行时异常)。

答案 6 :(得分:2)

我是这样做的:

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

然后空信号无效数据。如果您想要默认值,可以将其更改为:

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

答案 7 :(得分:1)

我认为最佳做法是您展示的代码。

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

答案 8 :(得分:1)

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

答案 9 :(得分:0)

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

答案 10 :(得分:0)

Cleaner语义(Java 8 OptionalInt)

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

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

如果你有很多字符串输入,你可以考虑返回IntStream而不是OptionalInt,这样你就可以flatMap()

参考

答案 11 :(得分:-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 ) 
          );
}

您可以在其中记录需求,并且可以创建定义明确且可测试的规则。

答案 12 :(得分:-1)

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

我使用了你的技巧,它很糟糕,因为我的嵌入式系统上的堆栈跟踪是打印的,无论你是否抓住它们:(

答案 13 :(得分:-2)

异常机制很有价值,因为它是将状态指示器与响应值结合使用的唯一方法。此外,状态指标是标准化的。如果出现错误,您会收到异常。这样您就不必自己想出错误指示器。 争议不是例外,而是与Checked Exceptions(例如您必须捕获或声明的那些)。

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

如果您不想处理异常,只需将其包装在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
    }
  }
}

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

答案 14 :(得分:-2)

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

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

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

然后在此处输入您的提案链接,我们都会投票支持!

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

答案 15 :(得分:-5)

在它前面加上一些if语句。 if(null!= userdata)