JavaScript阿拉伯數字與中文數字互相轉換

有一次在上海前端交流羣看見有人在羣裏發了一個求助信息:前端

請用JavaScript語言編寫一個函數,要求入口參數爲數字, 取值範圍是一位數整數,返回值是字符串,該函數的功能爲:返回該數字對應的漢字,例如:輸入數字6,返回漢字「六」;輸入數字9,返回漢字「九」。git

而後我立馬丟了一個之前我寫的一個轉中文數字的angular過濾器代碼函數

//- 小寫數字轉換成大寫, 只處理到[0 ~ 99]
  function numberConvertToUppercase() {
    return function(num) {
      num = Number(num);
      var upperCaseNumber = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '百', '千', '萬', '億'];
      var length = String(num).length;
      if (length == 1) {
        return upperCaseNumber[num];
      } else if (length == 2) {
        if (num == 10) {
          return upperCaseNumber[num];
        } else if (num > 10 && num < 20) {
          return '十' + upperCaseNumber[String(num).charAt(1)];
        } else {
          return upperCaseNumber[String(num).charAt(0)] + '十' + upperCaseNumber[String(num).charAt(1)].replace('零', '');
        }
      }
    }
  }

接下來就有人迴應:測試

  • wolf 你這種寫法要命了code

  • 才99 就這麼長, 若是 99999呢ip

而後我以項目當時需求就只到2位爲由迴應。後來本身去嘗試寫一個完整的轉換方法。嘗試了不少次老是有一些細節沒有考慮全。內存

通過屢次測試後下面給出一個我最終寫出的一個完整版本,供參考:字符串

/**
 * 阿拉伯數字轉中文數字,
 * 若是傳入數字時則最多處理到21位,超過21位js會自動將數字表示成科學計數法,致使精度丟失和處理出錯
 * 傳入數字字符串則沒有限制
 * @param {number|string} digit
 */
function toZhDigit(digit) {
  digit = typeof digit === 'number' ? String(digit) : digit;
  const zh = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九'];
  const unit = ['千', '百', '十', ''];
  const quot = ['萬', '億', '兆', '京', '垓', '秭', '穰', '溝', '澗', '正', '載', '極', '恆河沙', '阿僧祗', '那由他', '難以想象', '無量', '大數'];

  let breakLen = Math.ceil(digit.length / 4);
  let notBreakSegment = digit.length % 4 || 4;
  let segment;
  let zeroFlag = [], allZeroFlag = [];
  let result = '';

  while (breakLen > 0) {
    if (!result) { // 第一次執行
      segment = digit.slice(0, notBreakSegment);
      let segmentLen = segment.length;
      for (let i = 0; i < segmentLen; i++) {
        if (segment[i] != 0) {
          if (zeroFlag.length > 0) {
            result += '零' + zh[segment[i]] + unit[4 - segmentLen + i];
            // 判斷是否須要加上 quot 單位
            if (i === segmentLen - 1 && breakLen > 1) {
              result += quot[breakLen - 2];
            }
            zeroFlag.length = 0;
          } else {
            result += zh[segment[i]] + unit[4 - segmentLen + i];
            if (i === segmentLen - 1 && breakLen > 1) {
              result += quot[breakLen - 2];
            }
          }
        } else {
          // 處理爲 0 的情形
          if (segmentLen == 1) {
            result += zh[segment[i]];
            break;
          }
          zeroFlag.push(segment[i]);
          continue;
        }
      }
    } else {
      segment = digit.slice(notBreakSegment, notBreakSegment + 4);
      notBreakSegment += 4;

      for (let j = 0; j < segment.length; j++) {
        if (segment[j] != 0) {
          if (zeroFlag.length > 0) {
            // 第一次執行zeroFlag長度不爲0,說明上一個分區最後有0待處理
            if (j === 0) {
              result += quot[breakLen - 1] + zh[segment[j]] + unit[j];
            } else {
              result += '零' + zh[segment[j]] + unit[j];
            }
            zeroFlag.length = 0;
          } else {
            result += zh[segment[j]] + unit[j];
          }
          // 判斷是否須要加上 quot 單位
          if (j === segment.length - 1 && breakLen > 1) {
            result += quot[breakLen - 2];
          }
        } else {
          // 第一次執行若是zeroFlag長度不爲0, 且上一劃分不全爲0
          if (j === 0 && zeroFlag.length > 0 && allZeroFlag.length === 0) {
            result += quot[breakLen - 1];
            zeroFlag.length = 0;
            zeroFlag.push(segment[j]);
          } else if (allZeroFlag.length > 0) {
            // 執行到最後
            if (breakLen == 1) {
              result += '';
            } else {
              zeroFlag.length = 0;
            }
          } else {
            zeroFlag.push(segment[j]);
          }

          if (j === segment.length - 1 && zeroFlag.length === 4 && breakLen !== 1) {
            // 若是執行到末尾
            if (breakLen === 1) {
              allZeroFlag.length = 0;
              zeroFlag.length = 0;
              result += quot[breakLen - 1];
            } else {
              allZeroFlag.push(segment[j]);
            }
          }
          continue;
        }
      }
   

    --breakLen;
  }

  return result;
}

關於中文計數單位能夠網上自行搜索。get

上面的代碼大致思路是:string

從左至右,先把數字按萬分位分組,每組加上對應的單位(萬,億, ...), 而後每一個分組進行迭代。breakLen表示可以分紅多少個分組,notBreakSegment表示當前已處理過的分組長度。while循環中有一個if判斷,若是不存在result,則說明是第一次處理,那麼在處理上是有些不一樣的。首先,在segment的賦值上,第一次是從0開始,取notBreakSegment的長度,後面每迭代一次notBreakSegment都要在上一個值上加4;其次,第一次處理不用判斷上一個分組是否全爲0的情形,這裏zeroFlag表示每個分組內存在0的個數,allZeroFalg表示當前分組前面出現的全爲0的分組的個數。此外,在第一次執行時,還處理了只傳入爲0的情形。

每次處理segment[i]時,都要先判斷當前值是否爲0,爲0時則直接記錄到zeroFlag,而後進入下一次迭代,若是不爲0,首先得判斷上一個數字是否爲0, 而後還得根據上一個0是否位於上一個分組的末位,來添加quot,最後還須要清空標誌位。若是當前分組全爲0,則標記allZeroFlag,因此在下一個分組處理時,還須要判斷上一個分組是否全爲0

更多細節直接看代碼,這裏就很少做解釋了。

接下來是中文轉阿拉伯數字,這個處理起來比較簡單,這裏採用從右至左的方式對每一位進行迭代,直接上代碼:

function zhDigitToArabic(digit) {
  const zh = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九'];
  const unit = ['千', '百', '十'];
  const quot = ['萬', '億', '兆', '京', '垓', '秭', '穰', '溝', '澗', '正', '載', '極', '恆河沙', '阿僧祗', '那由他', '難以想象', '無量', '大數'];
  let result = 0, quotFlag;

  for (let i = digit.length - 1; i >= 0; i--) {
    if (zh.indexOf(digit[i]) > -1) { // 數字
      if (quotFlag) {
        result += quotFlag * getNumber(digit[i]);
      } else {
        result += getNumber(digit[i]);
      }
    } else if (unit.indexOf(digit[i]) > -1) { // 十分位
      if (quotFlag) {
        result += quotFlag * getUnit(digit[i]) * getNumber(digit[i - 1]);
      } else {
        result += getUnit(digit[i]) * getNumber(digit[i - 1]);
      }
      --i;
    } else if (quot.indexOf(digit[i]) > -1) { // 萬分位
      if (unit.indexOf(digit[i - 1]) > -1) {
        if (getNumber(digit[i - 1])) {
          result += getQuot(digit[i]) * getNumber(digit[i - 1]);
        } else {
          result += getQuot(digit[i]) * getUnit(digit[i - 1]) * getNumber(digit[i - 2]);
          quotFlag = getQuot(digit[i]);
          --i;
        }
      } else {
        result += getQuot(digit[i]) * getNumber(digit[i - 1]);
        quotFlag = getQuot(digit[i]);
      }
      --i;
    }
  }

  return result;

  // 返回中文大寫數字對應的阿拉伯數字
  function getNumber(num) {
    for (let i = 0; i < zh.length; i++) {
      if (zh[i] == num) {
        return i;
      }
    }
  }

  // 取單位
  function getUnit(num) {
    for (let i = unit.length; i > 0; i--) {
      if (num == unit[i - 1]) {
        return Math.pow(10, 4 - i);
      }
    }
  }

  // 取分段
  function getQuot(q) {
    for (var i = 0; i < quot.length; i++) {
      if (q == quot[i]) {
        return Math.pow(10, (i + 1) * 4);
      }
    }
  }
}

說明:代碼僅供參考,做者只寫了一些特殊數字和隨機數字進行測試,不能保證百分百準確,若是有問題請留言反饋。

相關文章
相關標籤/搜索