1. 程式人生 > >記一次bug解決過程(數字轉化成中文)

記一次bug解決過程(數字轉化成中文)

前言

由於公司業務週期較短,時常是幾個專案一起做,或是加上bug修復。上午的時候接到個任務,正式上的網站發現如下圖的錯誤。

表頭錯誤

問題原因

找到專案中對應頁面,很快發現了問題,原來之前的哥們通過定義了個數組,通過序號來取對應的中文。這樣的方式缺點很明顯,個數有限。導致現在出現不夠用的狀況。

解決方式

經過考慮覺得可以抽出一個公共的方法程式碼如下:

/**
 * 根據輸入的數字返回對應是中文,格式如:三十一
 *  @param {Number} index 序號
 */
export function covertNumberTochinese(index) {
  const multiple = parseInt((index + 1) / 10)
  const remainder = (index + 1) % 10
  const weekheadNum = [
    '一'
, '二', '三', '四', '五', '六', '七', '八', '九', '十' ] let text = '' const textMap = new Map([ [/^1_[0-9]$/, () => `十${weekheadNum[remainder - 1]}`], [/^1_0$/, () => `十`], [ /^[2-9]_[1-9]$/, () => `${weekheadNum[multiple - 1]}${weekheadNum[remainder - 1]}
` ], [/^[2-9]_0$/, () => `${weekheadNum[multiple - 1]}十`], [/^0_\w/, () => `${weekheadNum[remainder - 1]}`] ]) const textList = [...textMap].filter(([reg]) => { return reg.test(multiple + '_' + remainder) }) textList.forEach(([reg, callBack]) => { text = callBack() }) return
text } 複製程式碼
  1. multiple用來判斷有幾個10,remainder表示餘幾。
  2. 這裡使用Map資料結構的原因是因為將物件作為鍵名,這樣可以將正則與函式關聯起來。
  3. 使用正則匹配,可以匹配多種情況,使用正則的test方法來校驗是否匹配成功。  

 需要用到的知識點

  1. new Map([['鍵名':'鍵值']])
  2. ... 擴充套件運算子 遍歷 Iterator 介面(這裡是Map)
  3. [reg] = [/^1_[0-9]$/, () => `十${weekheadNum[remainder - 1]}`] 陣列結構賦值

分析

  1. covertNumberTochinese函式接受一個數值,對數值進行取餘 remainder(用來當作個位數)和商取整的multiple(用來當作十位數)。
  2. 將個位數十位數拼接成用_相連的字串,然後用正則取匹配這個字串。然後兩個中文字拼接起來。
  3. 需要注意的是當multiple為1,remainder為0時本應該返回'十零'但是我們習慣不是注意的叫法,多加一個判斷條件。
  4. 判斷下remainder(個位數)為0時,省去'幾十零'後面的零

小結

用上面的方法可以將數字匹配到中午 1-99位,雖然專案中是夠用了,但是侷限性還是很大,而且判斷的條件很多,邏輯不直觀。

尋求其他解決方案

/**
 * 根據輸入的數字返回對應是中文,格式如:一千零一
 *  @param {Number|String} index 序號或者數字開頭的字串
 */
  export numberToChinese(number) {
      number = String(parseInt(number))
      const ChineseText = '零一二三四五六七八九'
      const smallUnit = '十百千'
      const length = number.length
      let n = length - 2
      let string = ''
      for (var i = 0; i < length; i++) {
        let num = number.charAt(i)
        string += ChineseText.charAt(num)
        string += num > 0 ? smallUnit.charAt(n) : ''
        n--
      }
      return string
    }
複製程式碼

分析

  1. 首先將傳入的數字取整處理,然後將其轉換成字串,為了是使用字串的.charAt方法取對應位數的中文。
  2. 定義好中文的0-9,以及單位'十百千' (為了簡單起見,只討論四位數以下的轉換,位數增加方法是類似的)
  3. 定義好string是最終返回的中文字串,是通過拼接的方式得到一箇中文的數字,這裡n代表取第幾位單位也就是取smallUnit的第幾位

迴圈體中做的事

  1. 去除number(也就是數字字串如1001)的第一位,就是數字1型別是string型別,然後通過這個1取ChineseText中的中文得到中文的一
  2. 然後通過n取一對應的單位,這裡n=length-2是因為再定義單位時是從十開始的而不是從個位數開始,並且.charAt(0)取的是字串的第一位。並且當數字為零的時候不需要單位,所以加了num > 0的限定條件。

完善

就此數字轉換成中文的方法就寫完了,在瀏覽器中列印如下

1001
有多個零的時候我們習慣是一千零一,並且結尾為零的時候是省略的 所以 我們還得對返回的string做一下去零處理

/**
 * 根據輸入的字串返回按照規則去零後的字串,格式如:一千零一
 *  @param {string} str 中文數字 如 一千零零一
 */
export clearZero(str){
    const regMiddle = /零{2}/g
    const regEnd = /零?零$/
    str = str.replace(regMiddle, '零')
    return str.replace(regEnd, '')
}
複製程式碼
  1. 通過clearZero函式首先將匹配中間有兩個零相連的情況,轉為一個零
  2. 然後判斷結尾是否有一個至兩個零的情況,將它們清空
  3. 注意點: replace 不會改變原來的值,需要將操作後的結果重新賦值

將 numberToChinese 函式改為如下

export numberToChinese(number) {
      number = String(parseInt(number))
      const ChineseText = '零一二三四五六七八九'
      const smallUnit = '十百千'
      const length = number.length
      let n = length - 2
      let string = ''
      for (var i = 0; i < length; i++) {
        let num = number.charAt(i)
        string += ChineseText.charAt(num)
        string += num > 0 ? smallUnit.charAt(n) : ''
        n--
      }
      return  clearZero(string)
    }
複製程式碼

總結

  1. 四位數以下的數字轉換成中文的方法已經完成了 ,更多位數的轉換原理相同,有個方法就是將多位數的數字擷取成 四位為一組,分組處理,最後將結果拼接起來,這樣可能會減少一些邏輯判斷。
  2. 核心部分就是通過charAt取數字字串,然後通過陣列去找定義好的ChineseText對應的中文,定義了一個n來取當前位數對應的單位