/**
* Checks whether the String a valid Java number.
*
* Valid numbers include hexadecimal marked with the 0x
* qualifier, scientific notation and numbers marked with a type
* qualifier (e.g. 123L).
*
* Null
and empty String will return
* false
.
*
* @param str the String
to check
* @return true
if the string is a correctly formatted number
*/
public static boolean isNumber(String str) {
if (StringUtils.isEmpty(str)) {
return false;
}
char[] chars = str.toCharArray();
int sz = chars.length;
boolean hasExp = false;
boolean hasDecPoint = false;
boolean allowSigns = false;
boolean foundDigit = false;
// deal with any possible sign up front
int start = (chars[0] == '-') ? 1 : 0;
if (sz > start + 1) {
if (chars[start] == '0' && chars[start + 1] == 'x') {
int i = start + 2;
if (i == sz) {
return false; // str == "0x"
}
// checking hex (it can't be anything else)
for (; i < chars.length; i++) {
if ((chars[i] < '0' || chars[i] > '9')
&& (chars[i] < 'a' || chars[i] > 'f')
&& (chars[i] < 'A' || chars[i] > 'F')) {
return false;
}
}
return true;
}
}
sz--; // don't want to loop to the last char, check it afterwords
// for type qualifiers
int i = start;
// loop to the next to last char or to the last char if we need another digit to
// make a valid number (e.g. chars[0..5] = "1234E")
while (i < sz || (i < sz + 1 && allowSigns && !foundDigit)) {
if (chars[i] >= '0' && chars[i] <= '9') {
foundDigit = true;
allowSigns = false;
} else if (chars[i] == '.') {
if (hasDecPoint || hasExp) {
// two decimal points or dec in exponent
return false;
}
hasDecPoint = true;
} else if (chars[i] == 'e' || chars[i] == 'E') {
// we've already taken care of hex.
if (hasExp) {
// two E's
return false;
}
if (!foundDigit) {
return false;
}
hasExp = true;
allowSigns = true;
} else if (chars[i] == '+' || chars[i] == '-') {
if (!allowSigns) {
return false;
}
allowSigns = false;
foundDigit = false; // we need a digit after the E
} else {
return false;
}
i++;
}
if (i < chars.length) {
if (chars[i] >= '0' && chars[i] <= '9') {
// no type qualifier, OK
return true;
}
if (chars[i] == 'e' || chars[i] == 'E') {
// can't have an E at the last byte
return false;
}
if (chars[i] == '.') {
if (hasDecPoint || hasExp) {
// two decimal points or dec in exponent
return false;
}
// single trailing decimal point after non-exponent is ok
return foundDigit;
}
if (!allowSigns
&& (chars[i] == 'd'
|| chars[i] == 'D'
|| chars[i] == 'f'
|| chars[i] == 'F')) {
return foundDigit;
}
if (chars[i] == 'l'
|| chars[i] == 'L') {
// not allowing L with an exponent
return foundDigit && !hasExp;
}
// last character is illegal
return false;
}
// allowSigns is true iff the val ends in 'E'
// found digit it to make sure weird stuff like '.' and '1E-' doesn't pass
return !allowSigns && foundDigit;
}
org.apache.commons.lang.math.NumberUtils#isNumber 的作用是检查一个字符串是否可以被解析为一个有效的数字。这包括整数、浮点数、科学计数法表示的数以及十六进制数。下面是对代码的逐步解释:
初始检查:首先,使用 StringUtils.isEmpty(str)
检查输入字符串是否为空或为 null
。如果是,方法返回 false
。
处理十六进制数:接下来,代码检查字符串是否以 “0x” 开头,这表明它可能是一个十六进制数。如果是,它将遍历字符串的其余部分,确保每个字符都是有效的十六进制数字(0-9, a-f, A-F)。如果所有字符都有效,方法返回 true
。
设置循环变量和条件:代码设置了几个变量来跟踪解析过程中的状态,包括是否发现了指数部分 (hasExp
)、是否有小数点 (hasDecPoint
)、是否允许符号 (allowSigns
)、是否找到了数字 (foundDigit
)。循环从字符串的开始(或跳过了负号后的第一个字符)开始,直到字符串的倒数第二个字符,或者在需要另一个数字来形成有效数字时直到最后一个字符。
循环解析字符:在循环中,代码根据当前字符的类型更新状态变量。如果字符是数字,foundDigit
设置为 true
,allowSigns
设置为 false
。如果字符是小数点,检查是否已经有小数点或指数部分,如果有,则返回 false
。如果字符是 ‘e’ 或 ‘E’,检查是否已经有指数部分或之前没有找到数字,如果是,则返回 false
;否则,设置 hasExp
为 true
并允许后续出现符号。如果字符是 ‘+’ 或 ‘-’,检查是否允许符号,如果不允许,则返回 false
;否则,重置 allowSigns
和 foundDigit
。
检查最后一个字符:循环结束后,代码检查字符串的最后一个字符。如果是数字,返回 true
。如果是 ‘e’ 或 ‘E’,返回 false
,因为不能以指数符号结束。如果是小数点,检查是否已经有小数点或指数部分,如果没有且之前找到了数字,则返回 true
。如果是类型限定符(‘d’, ‘D’, ‘f’, ‘F’, ‘l’, ‘L’),根据之前的状态返回相应的结果。
返回结果:最后,如果没有找到不合法的情况,检查 allowSigns
和 foundDigit
的状态,如果没有挂起的指数符号且至少找到了一个数字,则返回 true
;否则,返回 false
。
总之,这段代码通过一系列的检查和状态跟踪,来判断一个字符串是否可以被解析为一个有效的数字。
下面是一些使用 NumberUtils.isNumber(String str)
方法的示例,以及每个示例的预期返回结果。这些示例展示了不同类型的字符串输入和该方法如何判断它们是否可以被解析为有效的数字。
NumberUtils.isNumber("123"); // true - 正整数
NumberUtils.isNumber("-123"); // true - 负整数
NumberUtils.isNumber("123.45"); // true - 正浮点数
NumberUtils.isNumber("-123.45"); // true - 负浮点数
NumberUtils.isNumber("0.123"); // true - 小于1的浮点数
NumberUtils.isNumber("-0.123"); // true - 小于0的负浮点数
NumberUtils.isNumber("123E4"); // true - 科学计数法
NumberUtils.isNumber("-123E4"); // true - 负的科学计数法
NumberUtils.isNumber("123.45E-6"); // true - 带小数点的科学计数法
NumberUtils.isNumber("0x1A"); // true - 十六进制数
NumberUtils.isNumber("0x1G"); // false - 无效的十六进制数('G' 不是十六进制字符)
NumberUtils.isNumber("123L"); // true - 长整型
NumberUtils.isNumber("NaN"); // false - 不是有效的数字格式
NumberUtils.isNumber("INF"); // false - 不是有效的数字格式
NumberUtils.isNumber(""); // false - 空字符串
NumberUtils.isNumber(null); // false - null 值
请注意,这些示例假设 NumberUtils.isNumber(String str)
方法的实现与之前讨论的代码逻辑一致。实际的返回结果可能会根据具体实现的细节有所不同。
NumberUtils.isCreatable(String str)
和 NumberUtils.isNumber(String str)
都是 Apache Commons Lang 库中的方法,用于判断一个字符串是否可以解析为一个数字。它们之间的主要区别在于它们对可解析数字的定义和容忍度。
NumberUtils.isNumber(String str)
(在Apache Commons Lang 3.4及之前的版本中使用):
true
。NumberUtils.isCreatable(String str)
(在Apache Commons Lang 3.5及之后的版本中引入):
简而言之,NumberUtils.isCreatable(String str)
是 NumberUtils.isNumber(String str)
的一个改进和扩展版本,提供了更广泛的数字格式支持和更严格的验证。如果你的项目使用的是 Apache Commons Lang 3.5 或更高版本,建议使用 NumberUtils.isCreatable(String str)
来判断字符串是否可以解析为数字。