實現數值校驗算法

語言: CN / TW / HK

前言

給定一個字符串如何判斷它是否為數值類型?例如:字符串+1005e2-1233.1416以及-1E-16都表示數值,為數值類型,但12e1a3.141.2.3+-5以及12e+5.4都不是。

本文將帶着大家實現這個判斷算法,歡迎各位感興趣的開發者閲讀本文。

實現思路

我們先來看一下數值的定義規則:表示數值的字符串遵循模式A[.[B]][e|EC]或者.B[e|EC],其中:

  • A為數值的整數部分
  • B緊跟着小數點為數值的小數部分
  • C緊跟着e或者E為數值的指數部分

在小數裏可能沒有數值的整數部分,例如:小數.123等於0.123。因此A部分不是必須的,如果一個數沒有整數部分,那麼它的小數部分不能為空。

上述A和C都是可能以+或者-開頭的0~9的數位串;B也是0~9的數位串,但前面不能有正負號。我們以字符串123.45e+6為例,其中:

  • 123是它的整數部分A
  • 45是它的小數部分B
  • +6是它的指數部分C

判斷一個字符串是否符合上述模式時,首先儘可能多地掃描0~9的數位(有可能起始處有+或者-),也就是前面模式中表示數值整數的A部分。如果遇到小數點.,則開始掃描表述數值小數部分的B部分。如果遇到e或者E,則開始掃描表示數值指數的C部分。

我們將上面所述整理下,就能列出實現思路了,如下所示:

  • 在字符串後添加結束標誌
  • 使用全局索引遍歷字符串
  • 設計一個函數用來掃描無符號整數(字符串中0~9的數位),用來判斷數值模式中的B部分。
  • 設計一個函數用來掃描可以表示正負的+或者-為起始的0~9的數位(類似於一個可能帶正負符號的整數),用來判斷數值模式中的A和C部分。
  • 從頭開始掃描字符串,跳過首部空格,掃一次全局索引自增一次
  • 調用掃描有符號整數函數來掃描A部分
  • 如果字符串中包含小數點.,則調用掃描無符號整數函數來掃描B部分
  • 如果字符串中包含E或者e,則調用掃描有符號整數函數來掃描C部分

  • 跳過尾部空格

  • 判斷校驗結果是否為true以及全局索引自增到了結束標識處

接下來,我們以123.45e+6為例,畫一下上述流程的執行過程,如下所示:

image-20220403221926906

實現代碼

萬事俱備,接下來,我們來看下代碼實現。

  • 掃描無符號整數函數的代碼如下所示:

```typescript export class NumericalCheck { // 指針索引 private index = 0;

// 掃描無符號整數 private scanUnsignedInteger(str: string): boolean { const before = this.index; while (str.charAt(this.index) >= "0" && str.charAt(this.index) <= "9") { this.index++; } return this.index > before; } } ```

  • 掃描有符號整數函數是在無符號的基礎上添加符號的判斷,其如下所示:

```typescript // 掃描有符號整數 private scanInteger(str: string): boolean { // 判斷其是否包含正負號 if (str.charAt(this.index) == "+" || str.charAt(this.index) == "-") { this.index++; }

// 掃描無符號整數
return this.scanUnsignedInteger(str);

} ```

  • 最後,從頭到尾遍歷字符串,結合上述兩個函數,判斷字符串是否為數值,其代碼如下所示:

```typescript public isNumber(numStr: string): boolean { if (numStr == null || numStr.length == 0) { return false; } // 添加結束標誌 numStr = numStr + "|"; // 跳過首部的空格 while (numStr.charAt(this.index) == " ") { this.index++; }

// 掃描整數部分
let numeric = this.scanInteger(numStr);

// 有小數點,處理小數部分
if (numStr.charAt(this.index) == ".") {
  this.index++;
  // 小數兩邊只要有一邊有數字即可,所以用||
  numeric = this.scanUnsignedInteger(numStr) || numeric;
}

// 有e||E,處理指數部分
if (numStr.charAt(this.index) == "E" || numStr.charAt(this.index) == "e") {
  this.index++;
  // e || E兩邊都要有數字,所以用&&
  numeric = numeric && this.scanInteger(numStr);
}

// 跳過尾部空格
while (numStr.charAt(this.index) == " ") {
  this.index++;
}
const checkResult = numeric && numStr.charAt(this.index) == "|";
// 重置指針索引
this.index = 0;
return checkResult;

} ```

完整代碼請移步:NumericalCheck.ts

測試用例

接下來,我們舉幾個例子,將其帶入上述代碼中,看下它能否正確執行,如下所示:

```typescript let str = "123.45e+6"; const numericalCheck = new NumericalCheck(); let checkResult = numericalCheck.isNumber(str); printCheckResult();

str = " .12e1 "; checkResult = numericalCheck.isNumber(str); printCheckResult();

str = "12e"; checkResult = numericalCheck.isNumber(str); printCheckResult();

str = "1.2.3"; checkResult = numericalCheck.isNumber(str); printCheckResult();

function printCheckResult() { console.log(字符串${str}是否為數值校驗結果為:${checkResult}); }

```

執行結果如下所示:

image-20220403223507337

示例代碼

文中所舉代碼的完整版請移步:

寫在最後

至此,文章就分享完畢了。

我是神奇的程序員,一位前端開發工程師。

如果你對我感興趣,請移步我的個人網站,進一步瞭解。

  • 文中如有錯誤,歡迎在評論區指正,如果這篇文章幫到了你,歡迎點贊和關注😊
  • 本文首發於神奇的程序員公眾號,未經許可禁止轉載💌