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

煙花散盡13141發表於2020-07-22

阿拉伯數字肯定是使用最廣泛的符合之一了。我們日常生活都離不開數字。中文下的數字是中國特色的數字。開發中也會遇到需要兩者相互轉換的需求。今天就來實現下。

中文數字轉阿拉伯數字

  • 想上面的數字一樣。假設我們的中文數字是【一億七千七十九萬七千一百九十七】。

解題思路

  • 首先我們需要將上面中文數字按阿拉伯數字進行分離。這裡我們先提出一個思想前提: 億、萬 這種是高階單位;千、百、十這種是低階單位。所以我們先按高階單位將上面的進行拆分。

  • 我們得到上面的三個數字。分別是【一億】、【七千零七十九萬】、【七千一百九十七】。下面我們針對這三個數字進行翻譯。

各個擊破

  • 仔細分析下上面三個中文數字,不難得出結論-上面三個數字去掉高階單位剩下的都只有低階單位了。這也是我們實現的重點【低階單位內的數字轉換】

  • 我們將三個數字分別轉換完成之後,剩下的就是將他們組合。組合的問題也就是中間補零和去零的操作。

  • 【七千零七十九萬】去掉高階單位【七千零七十九】我們翻譯也很簡單就是將低階單位去掉變成【七零七九】及【7079】。那麼上面的三位數字分別是

中文數字 去掉單位後 數字對映
一億 1
七千零七十九萬 七零七九 7079
七千一百九十七 七一九七 7197
  • 三位數字翻譯好之後我們進行阿拉伯數字的拼接就完成了。 170797197

友情提醒

  • 上面的數字翻譯的很是順利。但是又這麼一類數字總是事與願違。
  • 【一億零七十九萬零一百九十七】==【100790197】
  • 按照上面的思路我們會有如有推理
中文數字 去掉單位後 數字對映
一億 1
零七十九萬 零七九 079
零一百九十七 零一九七 0197
  • 根據表格我們拼接得到的數字是【10790197】。這個時候問題就大了去了。我們丟失了一位0。原因也很簡單在中文數字中在同一高階單位維度裡連續多個零的出現會同一讀零。但是阿拉伯數字中確實實實在在的佔位的。所以這裡就會缺失一位零。

  • 這裡的情況是一開始筆者沒喲注意到的。但是後來改正了。所以上面提到的合併方法是沒有問題的。上面是進行數字化乘以10000的。所以這裡的0也不會丟失的。

  • 正確的拼接方案是 ((110000)+079)10000+0197=100790197

  • 這種問題反向過來同樣值得注意,在阿拉伯轉中文數字的時候我們需要注意0到底代表幾個0

阿拉伯數字轉中文數字

  • 【100790197】 ==【一億零七十九萬零一百九十七】

  • 【100000197】 ==【一億零一百九十七】

  • 下面我們將對上面的兩個阿拉伯數字進行解析轉換。同樣我們對他們進行高位分離。這裡需要注意的是我們需要從低位到高位方向進行分離

  • 分離是從低位到高位。但是我們轉換卻要從高位到低位。順序不能錯。在我們阿拉伯數字轉中文數字的時候非零數字註解對映轉換就行了。但是遇到0的時候我們需要有一下三個條件才能轉換,否則就要忽略。

    • 第一次遇到0
    • 數字為0
    • 統一高位單元內後面並不是全是0
  • 簡單解釋下上面三個。第一個通過一個狀態為就能搞定。第二個其實就是保險點。可有可無。最重要第三點遇零後後面不能全是0.這個時候我們才可以將當前位置0翻譯成零。

  • 每個非零數字翻譯後需要加上低階單位的。

  • 所以上面的0079我們翻譯的是【零七十九】

  • 但是零七九並不是真正的漢字數字。所以我們在每一次高位翻譯完成之後需要加上高位單位。所以【0079】==【零七十九萬】

  • 所以我們得出如下

阿拉伯數字 數字對映
1 一億
0079 零七十九萬
0197 零一百九十七
  • 所以【100790197】 ==【一億零七十九萬零一百九十七】
阿拉伯數字 數字對映
1 一億
0000
0197 零一百九十七
  • 在加入高階單位時我們需要判斷高階單位內數字是否有效。因為我們上面三個原因。所以0000對應的中文數字就是空字串。這裡我們認為是無效中文數字。所以萬單位也就沒有了。所以【100000197】==【一億零一百九十七】

測試

  • 光談理論不講實踐都是扯淡。下面我們需要對我們的設計進行驗證。如何驗證嗯。好在我們實現的【中文數字轉阿拉伯數字】、【阿拉伯數字轉中文數字】 。 那麼我們直接通過兩個方法相互轉換。看看最終是不是原來的資料就能驗證出來了。話不多說、上程式碼

Integer right = 0;
Integer total = 10000000;
List<Map<String, Object>> list = new ArrayList<>();
for (int i = 0; i < total; i++) {
    Integer number = MathUtil.getInstance().getRandom(1, 1000000000);
    //System.out.println(i);
    //Integer number = 400001989;
    String chinese = DigitUtil.getInstance().getNumberFromAlabo(number.toString());
    String alabo = DigitUtil.getInstance().getNumberFromChinese(chinese);
    boolean equals = alabo.equals(number.toString());
    if (equals) {
        right++;
    } else {
        Map<String, Object> map  = new HashMap<>();
        map.put("number", number);
        map.put("alabo", alabo);
        map.put("chinese", chinese);
        list.add(map);
    }
}
for (Map<String, Object> map : list) {
    System.out.println(map);
}
System.out.println("成功率:"+Double.valueOf(right/(double)total));

  • 測試後的正確率是1 。 也就是0差錯。
  • 歡迎指出錯誤。

原始碼


package org.zxhtom.utils;

import org.zxhtom.constant.ChineseNumber;

import java.util.*;

/**
 * @package org.zxhtom.utils
 * @Class DigitUtil
 * @Description 數字工具類
 * @Author zhangxinhua
 * @Date 19-7-2 下午3:47
 */
public class DigitUtil {
    private static DigitUtil util;

    public static DigitUtil getInstance() {
        if (null == util) {
            util = new DigitUtil();
        }
        return util;
    }

    /**
     * 中文數字轉阿拉伯數字
     *  一萬兩千三百五十四 --> 12354
     * @param chinese 阿拉伯數字
     * @return 中文數字
     */
    public String getNumberFromChinese(String chinese) {
        String result = "0";
        //將中文數字按四位進行擷取。這樣每一位裡只有單一
        List<String> lists = new ArrayList<>();
        //暫時未使用到
        int lastLevelIndex = 0;
        //迴圈遍歷,目的是將億萬進行分離
        for (int i =  ChineseNumber.highLevel.size()-1; i>=0; i--) {
            //判斷億萬單位出現索引
            int levelIndex = chinese.indexOf(ChineseNumber.highLevel.get(i));
            if (levelIndex>0) {
                //表示有單位索引 , 將單位前資料進行擷取裝入lists中。後面的繼續迴圈擷取
                lists.add(chinese.substring(0, levelIndex));
                chinese = chinese.substring(levelIndex+1);
            } else if (levelIndex == -1) {
                //表示已經是最低單位了,不超過萬。直接加入lists中 , 這裡情況對應的是高位分離後,次高位開頭是0的情況
                lists.add(ChineseNumber.number.get(0));
            } else if (levelIndex == 0) {
                while (levelIndex > 1) {
                    levelIndex--;
                    lists.add(ChineseNumber.number.get(0));
                }
                //直接加入
                lists.add(chinese);
            }
        }
        //針對分離的四位資料,進行單獨翻譯
        for (int i = 0; i < lists.size(); i++) {
            //未使用
            Integer highLevelIndex = lists.size() - i - 1;
            //獲取單後設資料
            String single = lists.get(i);
            //對單後設資料進行翻譯 。
            String nextResult = getNumberFromFChinese(single);
            //lists中每位都是4位數擷取 ,所以這裡需要乘以10000進行疊加
            Long next = Long.valueOf(Integer.valueOf(result) * (int)(Math.pow(10, 4)) + Integer.valueOf(nextResult));
            result = next.toString();
        }
        //將開頭0抹掉
        result = result.replaceFirst("^(0+)", "");
        return result;
    }

    /**
     * 通過中文數字獲取4位數阿拉伯數字
     * 萬以內的資料轉換
     * @param single
     * @return
     */
    private String getNumberFromFChinese(String single) {
        String result = "0";
        Integer highIndex = 1;
        for (int i = 0; i < single.length(); i++) {
            String str = String.valueOf(single.charAt(i));
            int unit = ChineseNumber.level.indexOf(str);
            int number = ChineseNumber.number.indexOf(str);
            if (unit == -1) {
                //當前數字是萬以內的單位即  千百十其中之一
                int next = 0;
                if (i < single.length() - 1) {
                    //如果不是最後一位,則需要考慮當前位的權重
                    next = ChineseNumber.level.indexOf(String.valueOf(single.charAt(i + 1)));
                }
                result=String.valueOf(Integer.valueOf(result)+number * (int) (Math.pow(10, next)));
            }
        }
        //權重疊加
        result = ""+Integer.valueOf(result) * (int) (Math.pow(10, highIndex - 1));
        return result;
    }

    /**
     * 阿拉伯數字轉中文數字
     * 12354 --> 一萬兩千三百五十四
     * @param alabo 阿拉伯數字
     * @return 中文數字
     */
    public String getNumberFromAlabo(String alabo) {
        String result = "";
        List<String> list = new ArrayList<>();
        for (int length = alabo.length()-1; length >= 0; length--) {
            list.add(String.valueOf(alabo.charAt(length)));
        }
        List<List<String>> lists = CollectionUtil.averageSize(list, 4);
        Collections.reverse(lists);
        if (CollectionUtil.isNotEmpty(lists)) {
            for (int index=0;index<lists.size();index++) {
                List<String> singleNumList = lists.get(index);
                //反轉集合
                Collections.reverse(singleNumList);
                //預設0 false
                Boolean zeroflag =false;
                String chinese = "";
                for (int j=0 ; j<singleNumList.size();j++) {
                    Integer number = Integer.valueOf(singleNumList.get(j));
                    if (number == 0 && !zeroflag && afterNotAllZero(singleNumList, j)) {
                        //第一次遇到0 且後面有小單位內並不是全為0
                        chinese += ChineseNumber.number.get(number);
                        //修改遇零狀態true
                        zeroflag = true;
                    } else if(number!=0) {
                        //對映出對應的中文數字
                        chinese += ChineseNumber.number.get(number) + ChineseNumber.level.get(singleNumList.size()-j-1);
                    }
                }
                if (index==lists.size()&&chinese.substring(0, 1).equals(ChineseNumber.number.get(0))) {
                    //條件永遠不成立。
                    chinese = chinese.substring(1);
                }
                //並不是全部為0,該高階單位才會生效
                if (chinese.length()>0&&!ChineseNumber.highLevel.contains(chinese.substring(chinese.length() - 1))) {
                    result += chinese + ChineseNumber.highLevel.get(lists.size() - 1 - index);
                }
            }
        }
        return result;
    }

    /**
     * 判斷singleNumList在j位置之後是否全是0
     * @param singleNumList
     * @param j
     * @return
     */
    private boolean afterNotAllZero(List<String> singleNumList, int j) {
        for (int i = j+1; i < singleNumList.size(); i++) {
            if (!"0".equals(singleNumList.get(i))) {
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        Integer right = 0;
        Integer total = 10000000;
        List<Map<String, Object>> list = new ArrayList<>();
        for (int i = 0; i < total; i++) {
            Integer number = MathUtil.getInstance().getRandom(1, 1000000000);
            //System.out.println(i);
            //Integer number = 400001989;
            String chinese = DigitUtil.getInstance().getNumberFromAlabo(number.toString());
            String alabo = DigitUtil.getInstance().getNumberFromChinese(chinese);
            boolean equals = alabo.equals(number.toString());
            if (equals) {
                right++;
            } else {
                Map<String, Object> map  = new HashMap<>();
                map.put("number", number);
                map.put("alabo", alabo);
                map.put("chinese", chinese);
                list.add(map);
            }
        }
        for (Map<String, Object> map : list) {
            System.out.println(map);
        }
        System.out.println("成功率:"+Double.valueOf(right/(double)total));
    }
}


加入戰隊

# 加入戰隊

微信公眾號

微信公眾號

相關文章