【剑指offer】面试题20:表示数值的字符串

题目:请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串      ”+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);
	}
}

猜你喜欢

转载自blog.csdn.net/pcwl1206/article/details/85911048