1. 程式人生 > >【劍指offer】面試題20:表示數值的字串

【劍指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);
	}
}