题目:请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串 ”+100”、”5e2”、”-123”、”3.1416”和”-1E-16”都表示数值。 但是”12e”、”1a3.14”、”1.2.3”、”+-5”和”12e+4.3”都不是。
合法的数值可以表示为A[.[B]][e|EC]或者.B[e|EC]。
其中A为整数部分,B为小数部分,C为e|E的指数部分。在纯小数的时候,可能没有整数部分,如小数.123等于0.123,是合法的。所以A部分不是必须的。
A和C都是整数,可以带符号,也可不带。而B是一个无符号整数。
以表示数值的字符串“123.45e+6”为例,“123”是它的整数部分A,“45”是它的小数部分B,“+6”是它的指数部分C。
判断一个字符串是否复合上诉模式时,首先尽可能多地扫描0~9的数位(有可能在起始处有‘+’ 或 ‘-’),也就是前面模式中表示数值的整数A部分。如果遇到小数点 ‘.’,则开始扫描表示数值小数部分的B部分。如果遇到‘e’或者‘E’,则开始扫描表示数值指数部分的C部分。
整个过程的代码实现如下:
public class ExpressNumByString_Demo2 {
private int index = 0;
public boolean isNumeric(char[] str){
if(str.length < 1){
return false;
}
// 整数部分
boolean flag = scanInteger(str);
// 如果出现'.',则接下来是数字的小数部分
if(index < str.length && str[index] == '.'){
index++;
// 下面一行代码用||的原因:
// 1.小数可以没有整数部分,如:.123等于0.123
// 2.小数点后面可以没有数字,如233.等于233.0
// 3.当然,小数点前面和后面都可以有数字,如:233.666
flag = scanUnsignedInteger(str) || flag;
}
// 如果出现'e'或者'E',则接下来是数字的整数部分
if(index < str.length && (str[index] == 'E' || str[index] == 'e')){
index++;
// 下面一行代码用&&的原因:
// 1.当e或E前面没有数字时,整个字符串不能表示数字,如:e1、.e1
// 2.当e或E后面没有整数时,整个字符串不能表示数字,如:12e、12e+5.4
flag = flag && scanInteger(str);
}
return flag && index == str.length;
}
// 用来扫描可以表示正负‘+’或者‘-’为起始的0~9的范围
private boolean scanInteger(char[] str) {
if(index < str.length && (str[index] == '+' || str[index] == '-')){
index++;
}
return scanUnsignedInteger(str);
}
// 用来扫描字符串中0~9的数位
private boolean scanUnsignedInteger(char[] str) {
int start = index;
while(index < str.length && str[index] >= '0' && str[index] <= '9'){
index++;
}
// 是否存在整数
return start < index;
}
public static void main(String[] args) {
ExpressNumByString_Demo2 n = new ExpressNumByString_Demo2();
char[] str = {'1','0','0'};
boolean numeric = n.isNumeric(str);
System.out.println(numeric);
}
}
-
其他解法1:
public class ExpressNumByString {
public boolean isNumeric(char[] str){
if(str == null){
return false;
}
// 标记:符号位、小数点以及 e/E是否出现过
boolean sign = false, decimal = false, hasE = false;
for(int i = 0; i < str.length; i++){
if(str[i] == 'e' || str[i] == 'E'){
// e/E 后面一定要跟数字
if(i == str.length - 1){
return false;
}
if(hasE){
// 不能同时存在两个e
return false;
}
hasE = true;
}else if(str[i] == '+' || str[i] == '-'){
// 第2次出现‘+、-’符号,则必须紧接在e之后
if(sign && str[i - 1] != 'e' && str[i - 1] != 'E'){
return false;
}
// 第1次出现‘+、-’符号,如果不是在字符串的开头,则也必须紧接在e之后
// 开头:i = 0,跟在e之后即str[i - 1] = e 或者 E
if(!sign && i > 0 && str[i - 1] != 'e' && str[i - 1] != 'E'){
return false;
}
sign = true;
}else if(str[i] == '.'){
// e后面不能跟小数部分,小数点不能出现两次
// .肯定出现在e/E之前,所以在此之前hasE都应该为false
if(hasE || decimal){
return false;
}
decimal = true;
}else if(str[i] < '0' || str[i] > '9'){
// 不合法字符
return false;
}
}
return true;
}
}
-
其他解法2:直接使用正则表达式【但是如果是现场面试的话一般不建议使用】
以下对正则进行解释:
[\\+\\-]? -> 正或负符号出现与否
\\d* -> 整数部分是否出现,如-.34 或 +3.34均符合
(\\.\\d+)? -> 如果出现小数点,那么小数点后面必须有数字;否则一起不出现
([eE][\\+\\-]?\\d+)? -> 如果存在指数部分,那么e或E肯定出现,+或-可以不出现,紧接着必须跟着整数;或者整个部分都不出现
public class ExpressNumByString_Demo3 {
/*
以下对正则进行解释:
[\\+\\-]? -> 正或负符号出现与否
\\d* -> 整数部分是否出现,如-.34 或 +3.34均符合
(\\.\\d+)? -> 如果出现小数点,那么小数点后面必须有数字;
否则一起不出现
([eE][\\+\\-]?\\d+)? -> 如果存在指数部分,那么e或E肯定出现,+或-可以不出现,
紧接着必须跟着整数;或者整个部分都不出现
*/
public boolean isNumeric(char[] str){
String string = String.valueOf(str);
String regex = "[\\+\\-]?\\d*(\\.\\d+)?([eE][\\+\\-]?\\d+)?";
return string.matches(regex);
}
}