在解析字符串之前,如何检查它是否为数字?
通常使用一个简单的用户自定义函数(即编写自己的“isNumeric”函数)来完成此操作。
例如:
public static boolean isNumeric(String str) {
try {
Double.parseDouble(str);
return true;
} catch(NumberFormatException e){
return false;
}
}
然而,如果你频繁调用这个函数,并且预计由于不是数字而导致许多检查失败,则该机制的性能将不佳,因为你依靠每次失败都抛出异常,这是一种相当昂贵的操作。
另一种方法可能是使用正则表达式来检查是否为数字的有效性:
public static boolean isNumeric(String str) {
return str.matches("-?\\d+(\\.\\d+)?"); //match a number with optional '-' and decimal.
}
请注意上述的正则表达式机制,如果你使用非阿拉伯数字(即0到9之外的数字),它将会失败。这是因为正则表达式中的"\d"部分只匹配[0-9],并不具备国际化数字意识。 (感谢OregonGhost指出!)
或者另一种选择是使用Java内置的java.text.NumberFormat对象来查看,在解析字符串后,解析器位置是否在字符串的末尾。如果是,则可以假定整个字符串是数字:
public static boolean isNumeric(String str) {
ParsePosition pos = new ParsePosition(0);
NumberFormat.getInstance().parse(str, pos);
return str.length() == pos.getIndex();
}
.
将匹配任何字符,而不仅仅是小数点分隔符。 - jqnoNumberUtils.isCreatable
或 StringUtils.isNumeric
。NumberUtils.isNumber
或 StringUtils.isNumeric
。StringUtils.isNumericSpace
,它会返回空字符串的true
并忽略字符串中的内部空格。另一种方法是使用NumberUtils.isParsable
,它基本上检查数字是否符合Java的可解析规则。(链接的javadoc包含每种方法的详细示例。)StringUtils.isNumeric()
可能不太合适,因为它只检查字符串是否为数字序列。对于大多数整数来说还好,但对于带有小数、组分隔符等的数字则不适用。 - Jeff MercadoStringUtils
不支持前导符号,但你可以检查NumberUtils.isCreatable
,它可以正确处理负数。 - palacsintJava 8 Lambda 表达式。
String someString = "123123";
boolean isNumeric = someString.chars().allMatch( Character::isDigit );
如果您使用的是安卓系统,那么您应该使用:
android.text.TextUtils.isDigitsOnly(CharSequence str)
保持简单。大多数人都能“重新编程”(同一件事)。
正如@CraigTP在他出色的回答中提到的那样,我也对使用异常来测试字符串是否为数字存在类似的性能问题。因此我最终选择拆分字符串并使用java.lang.Character.isDigit()
。
public static boolean isNumeric(String str)
{
for (char c : str.toCharArray())
{
if (!Character.isDigit(c)) return false;
}
return true;
}
根据Javadoc,Character.isDigit(char)
能够正确识别非拉丁数字。就性能而言,我认为简单比较字符串中每个字符的数量N会比进行正则表达式匹配更加高效。
更新:如Jean-François Corbett 在评论中所指出的,上述代码仅验证正整数,这已经覆盖了大多数情况。下面是更新后的代码,根据系统中使用的默认语言环境正确验证小数,假设字符串中只有一个小数分隔符。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;
}
boolean isMinus = str.charAt(0) == localeMinusSign; if ((isMinus && str.length() < 2) || ((!isMinus) && !Character.isDigit(str.charAt(0)))) { return false; }
- codertoCharArray()
方法会在 String 对象中创建该数组的副本,因为 String 是不可变的。直接使用 String 对象上的 charAt(int index)
方法可能更快。 - Mike Kuceraif(str.length() == 0) return false;
来修复。 - samgakGoogle的Guava库提供了一个很好的助手方法来实现这一点:Ints.tryParse
。你可以像使用Integer.parseInt
一样使用它,但如果字符串不能解析为有效的整数,则它将返回null
而不是抛出异常。请注意它返回的是Integer而不是int,因此你需要将其转换/自动装箱回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上面的答案类似但略有不同。在它们的实现下,没有异常处理开销。
不要使用异常来验证您的值。 而是使用像apache NumberUtils这样的工具库:
NumberUtils.isNumber(myStringValue);
编辑:
请注意,如果你的字符串以0开头,NumberUtils会将其解释为十六进制值。
NumberUtils.isNumber("07") //true
NumberUtils.isNumber("08") //false
Number.isNumber()
。我不做任何解释,只进行翻译。 - Andy ThomasNumber.isNumber()
函数在回答的第一个版本中就存在了,该版本的时间为2012年9月24日17:01。 - Andy Thomas为什么每个人都在推动异常/正则表达式的解决方案?
虽然我理解大多数人使用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;
最后一点,我对被所有投票接受的示例中的赋值运算符感到好奇。添加赋值
double d = Double.parseDouble(...)
这不仅没有用处,因为您甚至没有使用该值,而且还浪费了处理时间,并使运行时间增加了几个纳秒(导致测试中增加了100-200毫秒)。我看不出为什么有人会这样做,因为这实际上是降低性能的额外工作。
你会认为编译器会优化掉它...虽然也许我应该检查字节码并看看编译器正在做什么。如果它确实被优化掉了,那么这就解释了为什么它对我总是表现得更冗长...因此我想知道怎么回事。请注意:通过“更冗长”,我指运行10000000次迭代的测试,并多次运行该程序(10倍以上),始终显示它的速度较慢。
编辑:更新了Character.isDigit()的一个测试
public static boolean isNumeric(String str)
{
return str.matches("-?\\d+(.\\d+)?");
}
CraigTP的正则表达式(如上所示)会产生一些误报。例如:"23y4"将被视为数字,因为'.'匹配任何字符而不是小数点。
此外,它会拒绝带前导'+'的任何数字。
另一个避免这两个小问题的替代方法是:
public static boolean isNumeric(String str)
{
return str.matches("[+-]?\\d*(\\.\\d+)?");
}
true
,对于单个加号"+"
或减号"-"
,以及对于"0."
则返回false
。 - user85421matches("-?\\d+([.]\\d+)?")
。 - Bobs我们可以尝试用空格("")替换给定字符串中的所有数字,如果替换后字符串的长度为零,则可以判断该字符串只包含数字。
boolean isNumber(String str){
if(str.length() == 0)
return false; //To check if string is empty
if(str.charAt(0) == '-')
str = str.replaceFirst("-","");// for handling -ve numbers
System.out.println(str);
str = str.replaceFirst("\\.",""); //to check if it contains more than one decimal points
if(str.length() == 0)
return false; // to check if it is empty string after removing -ve sign and decimal point
System.out.println(str);
return str.replaceAll("[0-9]","").length() == 0;
}
""
是一个数字,但 "3.14"
和 "-1"
不是? - Eric Duminil
Integer.parseInt()
的解决方案都无法正确解析包含“NumberFormatException”的移动电话号码。 - Not a bug