看到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;对于完全无上下文的代码片段来自应用程序的应用程序来说,它是完全正确的选项。
答案 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)
对于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)