羅馬數(shù)字轉(zhuǎn)換工具-java

package com.rc.components.study.arithmetic;

import org.junit.Test;

/**
 * 羅馬數(shù)字
 * 
 * 字符:
 * 基本字符     I   V   X   L   C   D   M
 * 阿拉伯?dāng)?shù)字    1   5   10  50  100 500 1000
 * 
 * 計(jì)數(shù)方法:
 * 1泥从、相同的數(shù)字連寫,所表示的數(shù)等于這些數(shù)字相加得到的數(shù)锭碳,如:Ⅲ = 3允悦;
 * 2失受、小的數(shù)字在大的數(shù)字的右邊哎壳,所表示的數(shù)等于這些數(shù)字相加得到的數(shù)趴樱, 如:Ⅷ = 8醋奠;Ⅻ = 12;
 * 3伊佃、小的數(shù)字,(限于Ⅰ沛善、X 和C)在大的數(shù)字的左邊航揉,所表示的數(shù)等于大數(shù)減小數(shù)得到的數(shù),如:Ⅳ= 4金刁;Ⅸ= 9帅涂;
 * 4议薪、正常使用時(shí),連寫的數(shù)字重復(fù)不得超過三次媳友。(表盤上的四點(diǎn)鐘“IIII”例外)
 * 5斯议、在一個(gè)數(shù)的上面畫一條橫線,表示這個(gè)數(shù)擴(kuò)大1000倍醇锚。
 * 
 * 原則:
 * 1哼御、基本數(shù)字Ⅰ、X 焊唬、C 中的任何一個(gè)恋昼,自身連用構(gòu)成數(shù)目,或者放在大數(shù)的右邊連用構(gòu)成數(shù)目赶促,都不能超過三個(gè)液肌;放在大數(shù)的左邊只能用一個(gè)。
 * 2鸥滨、不能把基本數(shù)字V 嗦哆、L 、D 中的任何一個(gè)作為小數(shù)放在大數(shù)的左邊采用相減的方法構(gòu)成數(shù)目婿滓;放在大數(shù)的右邊采用相加的方式構(gòu)成數(shù)目老速,只能使用一個(gè)。
 * 3空幻、V 和X 左邊的小數(shù)字只能用Ⅰ烁峭。
 * 4、L 和C 左邊的小數(shù)字只能用X秕铛。
 * 5约郁、D 和M 左邊的小數(shù)字只能用C。
 * 
 * 示例:
 * 個(gè)位數(shù)舉例Ⅰ,1 】Ⅱ但两,2】 Ⅲ鬓梅,3】 Ⅳ,4 】Ⅴ谨湘,5 】Ⅵ绽快,6】Ⅶ,7】 Ⅷ紧阔,8 】Ⅸ坊罢,9 】
 * 十位數(shù)舉例Ⅹ,10】 Ⅺ擅耽,11 】Ⅻ活孩,12】 XIII,13】 XIV,14】 XV,15 】XVI,16 】XVII,17 】XVIII,18】 XIX,19】 XX,20】 XXI,21 】XXII,22 】XXIX,29】 XXX,30】 XXXIV,34】 XXXV,35 】XXXIX,39】 XL,40】 L,50 】LI,51】 LV,55】 LX,60】 LXV,65】 LXXX,80】 XC,90 】XCIII,93】 XCV,95 】XCVIII,98】 XCIX,99 】
 * 百位數(shù)舉例C,100】 CC,200 】CCC,300 】CD,400】 D,500 】DC,600 】DCC,700】 DCCC,800 】CM,900】 CMXCIX,999】
 * 千位數(shù)舉例M,1000】 MC,1100 】MCD,1400 】MD,1500 】MDC,1600 】MDCLXVI,1666】 MDCCCLXXXVIII,1888 】MDCCCXCIX,1899 】MCM,1900 】MCMLXXVI,1976】 MCMLXXXIV,1984】 MCMXC,1990 】MM,2000 】MMMCMXCIX,3999】
 * 
 * @author: rc
 * @date: 2018年3月3日 下午7:42:47
 * @version: V1.0
 * @review: rc/2018年3月3日 下午7:42:47
 */
public class RomanNumeral {
    
    private final String stringValue;
    private final int intValue;
    
    public RomanNumeral(String romanStr){
        this.stringValue = romanStr;
        this.intValue = intValue(romanStr);
    }
    
    /**
     * 獲取羅馬字符串
     * @return
     */
    public String stringValue() {
        return stringValue;
    }
    /**
     * 獲取阿拉伯?dāng)?shù)字值
     * @return
     */
    public int intValue() {
        return intValue;
    }

    /**
     * 羅馬數(shù)字轉(zhuǎn)阿拉伯?dāng)?shù)字
     * @param romanStr
     * @return
     */
    public static int intValue(String romanStr){
        if (romanStr.isEmpty()) {
            throw new IllegalArgumentException("非法的羅馬數(shù)字格式");
        }
        
        int result = 0; //總數(shù)值
        int repeatLength = 1; //一個(gè)字母連續(xù)重復(fù)出現(xiàn)的長度
        int place = 0;  //當(dāng)前已經(jīng)計(jì)算出的位數(shù)
        char[] charArray = romanStr.toCharArray();
        
        for (int i = charArray.length - 1; i >= 0; i--) { //從右往左遍歷
            EBaseNumeral numeral =EBaseNumeral.build(charArray[i]);
            if (numeral.getPlace() <= place) { //后面出現(xiàn)的字符位數(shù)應(yīng)該大于前面已經(jīng)確定了的最大位數(shù)
                throw new IllegalArgumentException("非法的羅馬數(shù)字格式");
            }
            if (i == 0) { //只剩下最后一個(gè)字符時(shí),直接加上結(jié)果值乖仇,計(jì)算完成
                place = numeral.getPlace(); //進(jìn)位
                result += numeral.getN();
                break;
            } 
            EBaseNumeral numeralAfter = EBaseNumeral.build(charArray[i-1]); //獲取下一個(gè)字符
            if (!numeral.isRepeatable()) { //5開頭的數(shù)字
                if (numeral.equals(numeralAfter)) { //下一個(gè)字符相同
                    throw new IllegalArgumentException("非法的羅馬數(shù)字格式");
                } else if (numeral.getPlace() < numeralAfter.getPlace()) { //下一個(gè)字符的位數(shù)大
                    place = numeral.getPlace(); //進(jìn)位
                    result += numeral.getN();
                } else if (numeral.getPlace() == numeralAfter.getPlace()) { //下一個(gè)字符的位數(shù)不變并且是1開頭的數(shù)字
                    place = numeral.getPlace(); //進(jìn)位
                    result += numeral.getN() - numeralAfter.getN();
                    i--;
                } else { //其他情況均非法
                    throw new IllegalArgumentException("非法的羅馬數(shù)字格式");
                }
            } else { //1字開頭
                if (numeral.equals(numeralAfter)) { //下一個(gè)字符相同
                    if (repeatLength >= 3) { //字符連續(xù)出現(xiàn)次數(shù)超過3次
                        throw new IllegalArgumentException("非法的羅馬數(shù)字格式");
                    }
                    repeatLength++;
                    result += numeral.getN();
                    continue;
                } else if (numeral.getPlace() < numeralAfter.getPlace()){ //下一個(gè)字符的位數(shù)大
                    place = numeral.getPlace(); //進(jìn)位
                    repeatLength = 0;
                    result += numeral.getN();
                } else if (numeral.getPlace() == numeralAfter.getPlace()) { //下一個(gè)字符的位數(shù)不變并且是5開頭的數(shù)字
                    place = numeral.getPlace(); //進(jìn)位
                    repeatLength = 0;
                    result += numeral.getN() + numeralAfter.getN();
                    i--;
                } else if (numeral.getPlace() > numeralAfter.getPlace() && numeral.getN() == numeralAfter.getN() *10) { //下一個(gè)字符位數(shù)變10倍的變小
                    place = numeralAfter.getPlace(); //進(jìn)位
                    repeatLength = 0;
                    result += numeral.getN() - numeralAfter.getN();
                    i--;
                } else {
                    throw new IllegalArgumentException("非法的羅馬數(shù)字格式");
                }
            }
        }
        return result;
    }
    
    /**
     * @param intNumeral 只支持1-3999的數(shù)字轉(zhuǎn)換
     * @return
     */
    public static String stringValue(int numberal){
        if (numberal < 1 || numberal > 3999) {
            throw new IllegalArgumentException("非法的羅馬數(shù)字格式");
        }
        int baseNumberal = 0; //當(dāng)前位的數(shù)字
        int place = 1; //位數(shù)
        String stringValue = "";
        while (numberal > 0) {
            baseNumberal = numberal % 10; // 獲取位數(shù)
            switch (baseNumberal * place) {
                case 0: break;
                case 1: stringValue = "I" + stringValue; break;
                case 2: stringValue = "II" + stringValue; break;
                case 3: stringValue = "III" + stringValue; break;
                case 4: stringValue = "IV" + stringValue; break;
                case 5: stringValue = "V" + stringValue; break;
                case 6: stringValue = "VI" + stringValue; break;
                case 7: stringValue = "VII" + stringValue; break;
                case 8: stringValue = "VIII" + stringValue; break;
                case 9: stringValue = "IX" + stringValue; break;
                case 10: stringValue = "X" + stringValue; break;
                case 20: stringValue = "XX" + stringValue; break;
                case 30: stringValue = "XXX" + stringValue; break;
                case 40: stringValue = "XL" + stringValue; break;
                case 50: stringValue = "L" + stringValue; break;
                case 60: stringValue = "LX" + stringValue; break;
                case 70: stringValue = "LXX" + stringValue; break;
                case 80: stringValue = "LXXX" + stringValue; break;
                case 90: stringValue = "XC" + stringValue; break;
                case 100: stringValue = "C" + stringValue; break;
                case 200: stringValue = "CC" + stringValue; break;
                case 300: stringValue = "CCC" + stringValue; break;
                case 400: stringValue = "CD" + stringValue; break;
                case 500: stringValue = "D" + stringValue; break;
                case 600: stringValue = "DC" + stringValue; break;
                case 700: stringValue = "DCC" + stringValue; break;
                case 800: stringValue = "DCCC" + stringValue; break;
                case 900: stringValue = "CM" + stringValue; break;
                case 1000: stringValue = "M" + stringValue; break;
                case 2000: stringValue = "MM" + stringValue; break;
                case 3000: stringValue = "MMM" + stringValue; break;
                default: throw new IllegalArgumentException("非法的羅馬數(shù)字格式");
            }
            numberal /= 10;
            place *= 10;
        }
        return stringValue;
        
    }
    
    
    //羅馬數(shù)字基本字符
    private enum EBaseNumeral{
        
        I('I',1,1,true),
        V('V',5,1,false),
        X('X',10,2,true),
        L('L',50,2,false),
        C('C',100,3,true),
        D('D',500,3,false),
        M('M',1000,4,true),
        ;
        
        char c; //羅馬字符
        int n;  //對應(yīng)的阿拉伯?dāng)?shù)值
        int place;  //所在的位數(shù):1表示個(gè)位憾儒,2表示10位询兴,3表示百位,4表示千位
        boolean repeatable; //當(dāng)前字符可否連續(xù)多次出現(xiàn)起趾,只有IXCM可以連續(xù)多次出現(xiàn)诗舰,VLD只能出現(xiàn)一次,即5開頭的數(shù)字
        
        EBaseNumeral(char c, int n, int place, boolean repeatable){
            this.c = c;
            this.n = n;
            this.place = place;
            this.repeatable = repeatable;
        }

        public char getC() {
            return c;
        }

        public int getN() {
            return n;
        }

        public int getPlace() {
            return place;
        }

        public boolean isRepeatable() {
            return repeatable;
        }
        
        public static EBaseNumeral build(char c){
            switch (c) {
                case 'I': return I;
                case 'V': return V;
                case 'X': return X;
                case 'L': return L;
                case 'C': return C;
                case 'D': return D;
                case 'M': return M;
                default: throw new IllegalArgumentException("非法的羅馬數(shù)字格式");
            }
        }
    }
    
    @Test
    public static void test(){
        int i = 0;
        while (i++ < 3999) {
            System.out.print("numberal=" + i);
            String s = null;
            try {
                s = stringValue(i);
                System.out.print(", stringValue()=" + s);
            } catch (Exception e) {
                System.out.print(", stringValue()=" + e.getMessage());
            }
            try {
                int n = intValue(s);
                System.out.print(", intValue()=" + n);
            } catch (Exception e) {
                System.out.print(", intValue()=" + e.getMessage());
            }
            System.out.println();
        }
    }
    
    public static void main(String[] args) {
        test();
        
    }

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末训裆,一起剝皮案震驚了整個(gè)濱河市眶根,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌缭保,老刑警劉巖汛闸,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異艺骂,居然都是意外死亡诸老,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進(jìn)店門钳恕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來别伏,“玉大人,你說我怎么就攤上這事忧额±灏梗” “怎么了?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵睦番,是天一觀的道長类茂。 經(jīng)常有香客問我,道長托嚣,這世上最難降的妖魔是什么巩检? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮示启,結(jié)果婚禮上兢哭,老公的妹妹穿的比我還像新娘。我一直安慰自己夫嗓,他們只是感情好迟螺,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著舍咖,像睡著了一般矩父。 火紅的嫁衣襯著肌膚如雪贸桶。 梳的紋絲不亂的頭發(fā)上蚀瘸,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天安接,我揣著相機(jī)與錄音滴某,去河邊找鬼墓拜。 笑死羡鸥,一個(gè)胖子當(dāng)著我的面吹牛咬摇,可吹牛的內(nèi)容都是我干的儡陨。 我是一名探鬼主播辙诞,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼辙售,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了飞涂?” 一聲冷哼從身側(cè)響起旦部,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎较店,沒想到半個(gè)月后士八,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡梁呈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年婚度,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片官卡。...
    茶點(diǎn)故事閱讀 39,919評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蝗茁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出寻咒,到底是詐尸還是另有隱情哮翘,我是刑警寧澤,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布毛秘,位于F島的核電站饭寺,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏叫挟。R本人自食惡果不足惜艰匙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望霞揉。 院中可真熱鬧旬薯,春花似錦、人聲如沸适秩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽秽荞。三九已至骤公,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間扬跋,已是汗流浹背阶捆。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人洒试。 一個(gè)月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓倍奢,卻偏偏與公主長得像,于是被迫代替她去往敵國和親垒棋。 傳聞我的和親對象是個(gè)殘疾皇子卒煞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,864評論 2 354

推薦閱讀更多精彩內(nèi)容