阿拉伯-漢字-數字轉換

flybywind發表於2019-02-16

說明

本文實現了一個從阿拉伯數字到中文數字,以及從中文數字到阿拉伯數字的轉換演算法。
同時用Vuejs和Angularjs同時實現了一遍,對比了一下這兩個框架的優劣。在本例中,Vuejs的方便靈活性完勝Angularjs。

原始碼在這裡

阿拉伯數字轉中文

給定一個阿拉伯數字,把它轉變為漢語表示的數字。

演算法

根據中文的計數方法,可以把阿拉伯數字按4個一組分成若干section,每個section從低到高的單位分別為 “”,“萬”,“億”,“萬億”。

每個section內的轉換方法是一樣的,比如1234,就是”一千二百三十四”,加上對應的單位,如“萬”,就是“一千二百三十四萬”。但是在其中又有些細節需要注意:

  • 結尾的零都忽略,如1200,就是一千二百

  • 中間的零,只需要用一個零表示,如1004,是一千零四

  • 如果整個section都是0,全部忽略

  • 如果section在10到19之間,則十位可以省略一;否則,十位上的“一”都不能省。如12就是“十二”,312就是“三百一十二”

中文轉阿拉伯數字

給定一箇中文數字,如“一千二百三十四萬”,把它們轉換成阿拉伯數字

演算法

跟上面類似,以“萬”,“億”,“萬億”為分割位,先把中文分成若干section,每個section的轉換方法一樣,然後section數值乘以相應的權重,如section為“萬”就是乘以10000,“億”是乘以100000000,最低位的section,權重就是1。
將每個section的結果累加就是最終結果。

每個section最多8個漢字,都是以“數字+單位”的形式成對出現。將數字*單位的結果累加起來就是最終結果。單位從低到高就是1,10, 100, 1000。

需要注意的細節有:

  • “零”忽略即可

  • 如果第一位是“十”,則數字預設為一。

實現細節

分別通過Vuejs和angularjs實現了一遍,正好可以對比一下二者的不同。大部分程式碼二者都差不多。

但是為了方便閱讀,我對阿拉伯數字採用了千分位分隔,當使用者在輸入框中輸入完成按下回車後,輸入框內的阿拉伯數字自動用逗號分隔。差異注意出現在這個實現方法上。

Vuejs實現

// html檢視:
<input type="text" v-model="ArabNum|thousandth">

// js邏輯:
var app = new Vue({
    el: "#app",
    data: {
        ArabNum: 0,
        ChineseNum: "",
        //ChineseSymbole: ["零", "壹", "貳", "叄", "肆", "伍", "陸", "柒", "捌", "玖"],
        ChineseSymbole: ["零", "一", "二", "三", "四", "五", "六", "七", "八", "九"],
        ChineseSection: ["", "萬", "億", "萬億"],
        //ChineseUnit: ["", "拾", "佰", "仟"],
        ChineseUnit: ["", "十", "百", "千"],
        ChineseUnitArab: {
            "零": 0,
            "一": 1,
            "二": 2,
            "三": 3,
            "四": 4,
            "五": 5,
            "六": 6,
            "七": 7,
            "八": 8,
            "九": 9,
            "十": 10,
            "百": 100,
            "千": 1000,
            "萬": 10000,
            "億": 100000000,
            "萬億": 1000000000000,
        }
    },
    // 區別主要在這裡:
    filters: {
        thousandth: {
            read: function(v) {
                var str = "";
                while (v > 0) {
                    var left = "" + (v % 1000);
                    v = Math.floor(v / 1000);
                    if (v > 0) {
                        while (left.length < 3) {
                            left = "0" + left;
                        }
                    }
                    if (str == "") {
                        str = left;
                    } else {
                        str = left + "," + str;
                    }
                }
                return str;
            },
            write: function(v) {
                return parseInt(v.replace(/,/g, ""));
            },
        },
    },
    methods: {
        Arab2Chn: function(v) { ... },
        Arab2Chn_section: function(v) { ... },
        Chinese2Arab: function(str) { ... },
        Chinese2Arab_section: function(str) { ... },
    },
    computed: { ... },
})

Vuejs方便的地方在於,filter可以用在v-model中,只要分別設計read和write函式即可實現雙向繫結。read用於model到view的轉換,write用於view到model的轉換。

Angularjs實現

相比於Vuejs,如果在ng-model中直接使用filter,會報錯:

<input type="text" ng-model="ArabNum|thousandth" >
// error:
Expression `ArabNum|thousandth` is non-assignable

為了實現和Vuejs類似的邏輯,我首先google了一下,發現只能通過directive實現:

app.directive("thousandth", function() {
    return {
        require: "ngModel",
        link: function(scope, elm, attr, ngModel) {
            function thousandth(v) {
                var str = "",
                    v = "" + v;
                v = v.replace(/,/g, "");
                while (v > 0) {
                    var left = "" + (v % 1000);
                    v = Math.floor(v / 1000);
                    if (v > 0) {
                        while (left.length < 3) {
                            left = "0" + left;
                        }
                    }
                    if (str == "") {
                        str = left;
                    } else {
                        str = left + "," + str;
                    }
                }
                return str;
            }

            elm.on("blur", function(v) {
                elm.val(thousandth(ngModel.$modelValue));
            });
            // model -> view
            ngModel.$formatters.push(function(data) {
                return thousandth(data)
            })
            // view -> model
            ngModel.$parsers.push(function(v) {
                v = "" + v;
                return parseInt(v.replace(/,/g, ""));
            });
        }
    }
})

可以看到,首先ng的語法要麻煩很多。而且$formatters只有在內部的model被改變時,才會對view生效。比如input改變時,$parsers先呼叫(V–>M),返回值直接給了ngModel,但是$formatters不會呼叫,即M–>V不會被呼叫,否則 M修改V,V又修改M,就是死迴圈了。

只有你通過程式修改了ArabNum後,$formatters才會呼叫(M–>V)。所以,必須通過jquery的方式,在blur函式中,對element的value進行直接修改,才能實現上述Vue的效果。

總結

只要仔細一點,耐心一點,兩個數字表示方式的轉換還是比較容易實現的。

通過對比Vue和angular可以看到,Vue確實比Ng要方便些。這只是其中的一個方面。

相關文章