java - Java如何检查一个String是数值类型

  显示原文与译文双语对照的内容

在分析字符串之前,如何检查字符串是否是数字?

时间:

通常用简单的user-defined函数( 例如 。 Roll-your-own"isnumeric字节"函数) 。

就像这样:


public static boolean isNumeric(String str) 
{ 
 try 
 { 
 double d = Double.parseDouble(str); 
 } 
 catch(NumberFormatException nfe) 
 { 
 return false; 
 } 
 return true; 
}

但是,如果你多次调用这个函数,并且你期望许多检查失败,那么这个机制的性能将不会太好,因为每个故障都会抛出异常,这是一个相当昂贵的操作。

另一种方法可能是使用正则表达式检查作为数字的有效性:


public static boolean isNumeric(String str)
{
 return str.matches("-?d+(.d+)?");//match a number with optional '-' and decimal.
}

请小心使用上面的正规表达式 机制,因为如果你使用 non-latin ( 例如 。 0到 9位数字。 例如,阿拉伯数字。这是因为"d"正规表达式 只会匹配的一部分 [0-9] 有效并不是国际数字清楚。 ( 感谢OregonGhost指出这一点) !

甚至另一种替代方法是使用内置的java java.text.NumberFormat 对象来查看,在解析字符串之后解析器的位置在字符串末尾。 如果是,我们可以假定整个字符串是数字:


public static boolean isNumeric(String str)
{
 NumberFormat formatter = NumberFormat.getInstance();
 ParsePosition pos = new ParsePosition(0);
 formatter.parse(str, pos);
 return str.length() == pos.getIndex();
}

NumberUtils.isNumberStringUtils.isNumeric Apache Commons Lang 。

还可以使用 StringUtils.isNumericSpace 返回空字符串的true,并忽略字符串中的内部空格。 ( 链接的javadocs包含每个方法的详细示例。)

就像 @CraigTP 在他出色的回答中提到的,我也有类似的性能问题来测试字符串是否是数字或者非数字。 所以我结束了字符串并使用 java.lang.Character.isDigit()


public static boolean isNumeric(String str)
{
 for (char c : str.toCharArray())
 {
 if (!Character.isDigit(c)) return false;
 }
 return true;
}

根据Character.isDigit(char) 将正确识别non-Latin数字。 Performance-wise,我想一个简单的N 个比较数,其中N 是字符串中的字符数,比 正规表达式 匹配更高效。

下面是根据系统中使用的默认语言环境正确验证十进制数的更新代码,假定小数分隔符只出现在字符串中一次。


public static boolean isStringNumeric( String str )
{
 DecimalFormatSymbols currentLocaleSymbols = DecimalFormatSymbols.getInstance();
 char localeMinusSign = currentLocaleSymbols.getMinusSign();

 if (!Character.isDigit( str.charAt( 0 ) ) && str.charAt( 0 )!= localeMinusSign ) return false;

 boolean isDecimalSeparatorFound = false;
 char localeDecimalSeparator = currentLocaleSymbols.getDecimalSeparator();

 for ( char c : str.substring( 1 ).toCharArray() )
 {
 if (!Character.isDigit( c ) )
 {
 if ( c == localeDecimalSeparator &&!isDecimalSeparatorFound )
 {
 isDecimalSeparatorFound = true;
 continue;
 }
 return false;
 }
 }
 return true;
}

如果你在android上,那么你应该使用:


android.text.TextUtils.isDigitsOnly(CharSequence str)

文档可以在这里找到:

将它的保持为 大多数人都可以"re-program"( 同样的东西) 。

Guava 库的提供了一个不错的helper 方法来实现: Ints.tryParse 。你像 Integer.parseInt 一样使用它,但它返回 null 而不是在字符串解析为有效整数时引发异常。 注意它返回整数而不是整型,所以你必须将/autobox转换为 int 。

例如:


String s1 ="22";
String s2 ="22.2";
Integer oInt1 = Ints.tryParse(s1);
Integer oInt2 = Ints.tryParse(s2);

int i1 = -1;
if (oInt1!= null) {
 i1 = oInt1.intValue();
}
int i2 = -1;
if (oInt2!= null) {
 i2 = oInt2.intValue();
}

System.out.println(i1);//prints 22
System.out.println(i2);//prints -1

但是,在当前版本的-- Guava r11 --中,它仍然被标记为 @Beta.

我还没有对它进行基准测试。查看源代码有一些额外的检查,但是最终它们使用了 Character.digit(string.charAt(idx)) 上面的@Ibrahim 相似,但略有不同。 在它们的实现中,没有异常处理开销。


public static boolean isNumeric(String str)
{
 return str.matches("-?d+(.d+)?");
}

craigtp表达式( 上面显示)的正则表达式产生了一些错误的结果。 比如" 23 y4"将被算作数字,因为'。'匹配任何不是小数点的字符。

它也会拒绝任何带有前导'+'的数字'

避免这两个小问题的另一种方法是


public static boolean isNumeric(String str)
{
 return str.matches("[+-]?d*(.d+)?");
}

这是我对这个问题的回答。

catch所有便捷方法,你可以使用它来解析任何类型的解析器的字符串: isParsable(Object parser, String str) 解析器可以是 Class 或者 object 。 这也将允许你使用已经编写的自定义解析器,并应用于任何场景,例如:


isParsable(Integer.class,"11");
isParsable(Double.class,"11.11");
Object dateFormater = new java.text.SimpleDateFormat("yyyy.MM.dd G 'at' HH:mm:ss z");
isParsable(dateFormater,"2001.07.04 AD at 12:08:56 PDT");

下面是我的代码和方法描述。


import java.lang.reflect.*;

/**
 * METHOD: isParsable<p><p>
 * 
 * This method will look through the methods of the specified <code>from</code> parameter
 * looking for a public method name starting with"parse" which has only one String
 * parameter.<p>
 * 
 * The <code>parser</code> parameter can be a class or an instantiated object, eg:
 * <code>Integer.class</code> or <code>new Integer(1)</code>. If you use a
 * <code>Class</code> type then only static methods are considered.<p>
 * 
 * When looping through potential methods, it first looks at the <code>Class</code> associated
 * with the <code>parser</code> parameter, then looks through the methods of the parent's class
 * followed by subsequent ancestors, using the first method that matches the criteria specified
 * 上面.<p>
 * 
 * This method will hide any normal parse exceptions, but throws any exceptions due to
 * programmatic errors, eg: NullPointerExceptions, etc. If you specify a <code>parser</code>
 * parameter which has no matching parse methods, a NoSuchMethodException will be thrown
 * embedded within a RuntimeException.<p><p>
 * 
 * Example:<br>
 * <code>isParsable(Boolean.class,"true");<br>
 * isParsable(Integer.class,"11");<br>
 * isParsable(Double.class,"11.11");<br>
 * Object dateFormater = new java.text.SimpleDateFormat("yyyy.MM.dd G 'at' HH:mm:ss z");<br>
 * isParsable(dateFormater,"2001.07.04 AD at 12:08:56 PDT");<br></code>
 * <p>
 * 
 * @param parser The Class type or instantiated Object to find a parse method in.
 * @param str The String you want to parse
 * 
 * @return true if a parse method was found and completed without exception
 * @throws java.lang.NoSuchMethodException If no such method is accessible 
 */
public static boolean isParsable(Object parser, String str) {
 Class theClass = (parser instanceof Class? (Class)parser: parser.getClass());
 boolean staticOnly = (parser == theClass), foundAtLeastOne = false;
 Method[] methods = theClass.getMethods();

//Loop over methods
 for (int index = 0; index <methods.length; index++) {
 Method method = methods[index];

//If method starts with parse, is public and has one String parameter.
//If the parser parameter was a Class, then also ensure the method is static. 
 if(method.getName().startsWith("parse") &&
 (!staticOnly || Modifier.isStatic(method.getModifiers())) &&
 Modifier.isPublic(method.getModifiers()) &&
 method.getGenericParameterTypes().length == 1 &&
 method.getGenericParameterTypes()[0] == String.class)
 {
 try {
 foundAtLeastOne = true;
 method.invoke(parser, str);
 return true;//Successfully parsed without exception
 } catch (Exception exception) {
//If invoke problem, try a different method
/*if(!(exception instanceof IllegalArgumentException) &&
!(exception instanceof IllegalAccessException) &&
!(exception instanceof InvocationTargetException))
 continue;//Look for other parse methods*/

//Parse method refuses to parse, look for another different method
 continue;//Look for other parse methods
 }
 }
 }

//No more accessible parse method could be found.
 if(foundAtLeastOne) return false;
 else throw new RuntimeException(new NoSuchMethodException());
}


/**
 * METHOD: willParse<p><p>
 * 
 * A convienence method which calls the isParseable method, but does not throw any exceptions
 * which could be thrown through programatic errors.<p>
 * 
 * Use of {@link #isParseable(Object, String) isParseable} is recommended for use so programatic
 * errors can be caught in development, unless the value of the <code>parser</code> parameter is
 * unpredictable, or normal programtic exceptions should be ignored.<p>
 * 
 * See {@link #isParseable(Object, String) isParseable} for full description of method
 * usability.<p>
 * 
 * @param parser The Class type or instantiated Object to find a parse method in.
 * @param str The String you want to parse
 * 
 * @return true if a parse method was found and completed without exception
 * @see #isParseable(Object, String) for full description of method usability 
 */
public static boolean willParse(Object parser, String str) {
 try {
 return isParsable(parser, str);
 } catch(Throwable exception) {
 return false;
 }
}

如果你使用java开发 Android 应用,你可以使用 TextUtils.isDigitsOnly 函数。

你可以使用 NumberFormat#parse:


try
{
 NumberFormat.getInstance().parse(value);
}
catch(ParseException e)
{
//Not a number.
}

为什么每个人都在推异常/正规表达式 解决方案

虽然我可以理解大多数人都很善于使用 try/catch,但如果你经常这么做。。 这可能会非常的累人。

我在这里做的是获取 正规表达式,parseNumber() 方法和数组搜索方法,看看哪个是最有效的。 这次,我只看了整数。


public static boolean isNumericRegex(String str) {
 if (str == null)
 return false;
 return str.matches("-?d+");
}

public static boolean isNumericArray(String str) {
 if (str == null)
 return false;
 char[] data = str.toCharArray();
 if (data.length <= 0)
 return false;
 int index = 0;
 if (data[0] == '-' && data.length> 1)
 index = 1;
 for (; index <data.length; index++) {
 if (data[index] <'0' || data[index]> '9')//Character.isDigit() can go here too.
 return false;
 }
 return true;
}

public static boolean isNumericException(String str) {
 if (str == null)
 return false;
 try { 
/* int i = */Integer.parseInt(str);
 } catch (NumberFormatException nfe) { 
 return false; 
 }
 return true;
}

我得到的速度是:


Done with: for (int i = 0; i <10000000; i++)...

With only valid numbers ("59815833" and"-59815833"):
 Array numeric took 395.808192 ms [39.5808192 ns each]
 Regex took 2609.262595 ms [260.9262595 ns each]
 Exception numeric took 428.050207 ms [42.8050207 ns each]
//Negative sign
 Array numeric took 355.788273 ms [35.5788273 ns each]
 Regex took 2746.278466 ms [274.6278466 ns each]
 Exception numeric took 518.989902 ms [51.8989902 ns each]
//Single value ("1")
 Array numeric took 317.861267 ms [31.7861267 ns each]
 Regex took 2505.313201 ms [250.5313201 ns each]
 Exception numeric took 239.956955 ms [23.9956955 ns each]
//With Character.isDigit()
 Array numeric took 400.734616 ms [40.0734616 ns each]
 Regex took 2663.052417 ms [266.3052417 ns each]
 Exception numeric took 401.235906 ms [40.1235906 ns each]

With invalid characters ("5981a5833" and"a"):
 Array numeric took 343.205793 ms [34.3205793 ns each]
 Regex took 2608.739933 ms [260.8739933 ns each]
 Exception numeric took 7317.201775 ms [731.7201775 ns each]
//With a single character ("a")
 Array numeric took 291.695519 ms [29.1695519 ns each]
 Regex took 2287.25378 ms [228.725378 ns each]
 Exception numeric took 7095.969481 ms [709.5969481 ns each]

With null:
 Array numeric took 214.663834 ms [21.4663834 ns each]
 Regex took 201.395992 ms [20.1395992 ns each]
 Exception numeric took 233.049327 ms [23.3049327 ns each]
 Exception numeric took 6603.669427 ms [660.3669427 ns each] if there is no if/null check

声明:我没有声明这些方法是 100%优化的,它们只是用来演示数据

如果数字是 4个字符或者更少,则出现异常,并且每个字符串都是数字。 在这种情况下,为什么还要检查?

简而言之,如果使用 try/catch 频繁地遇到无效数字,这是非常痛苦的。 一个重要我一直遵循的原则是从未使用 try/catch 程序流。 这是一个例子。

有趣的是,简单的if char <0 ||> 9 编写起来非常简单,易于记住( 并且应该使用多种语言) 并赢得了几乎所有的测试场景。

唯一的缺点是我猜测 Integer.parseInt() 可能处理非ASCII数字,而数组搜索方法不。


对于那些想知道为什么我说很容易记住字符数组,如果你知道没有负号,你可以轻松地逃脱凝聚的东西:


public static boolean isNumericArray(String str) {
 if (str == null)
 return false;
 for (char c : str.toCharArray())
 if (c <'0' || c> '9')
 return false;
 return true;


最后 final 注意,我很好奇指定操作符在接受的例子中所有的选票。 正在添加赋值


double d = Double.parseDouble(...)

这不仅是无用的,因为你甚至没有使用这个值,而且它浪费了处理时间,并且增加了一些纳秒的时间。 我不知道为什么有人会这么做,因为它实际上是额外的工作来降低性能。

你认为这将被优化。 尽管我应该检查字节码,看看编译器正在做什么。 这并不解释为什么它总是为我显示为 lengthier,尽管如果它被优化了。 所以我想知道发生了什么。 注意:通过 lengthier,我的意思是运行 10000000次迭代的测试,并且多次运行该程序( 10 x+ ) 总是显示速度较慢。

编辑:为 Character.isDigit( ) 更新测试

...