有一次在上海前端交流羣看見有人在羣裏發了一個求助信息:前端
請用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); } } } }
說明:代碼僅供參考,做者只寫了一些特殊數字和隨機數字進行測試,不能保證百分百準確,若是有問題請留言反饋。