iT邦幫忙

0

【已解決】挑戰經典問題數字轉正楷中文-LinQ有更好的解法嗎?

經典題目: 輸入任數字介於 1~999999999 間,並將其轉成中文樣式

ex: 100 => 壹佰
    999999999 => 玖億玖仟玖佰玖拾玖萬玖仟玖佰玖拾玖

我的解法思路可以參考誤入C#村的『Java Programmer』經驗分享--LinQ
不過依照我第1~2步的做法所得到的結果

ex: GetAnswer(int 60058905) => 陸仟萬零佰萬零拾萬伍萬捌仟玖佰零拾伍

直覺應該可以直接用LinQ來對字串做操作來得到正確的中文結果/images/emoticon/emoticon19.gif

如果有半夜『熱』到睡不著覺,可挑戰看看這題/images/emoticon/emoticon39.gif

如何將 1~999999999 間的任意數字,從
『X億X仟萬X佰萬X拾萬X萬X仟X佰X拾X』變成正確的中文輸出
ex: 陸仟萬零佰萬零拾萬伍萬捌仟玖佰零拾伍 => 陸仟零伍萬捌仟玖佰零伍

燒完腦後會比較好入睡/images/emoticon/emoticon28.gif

雖然標題有提到『LinQ 有更好的解法嗎?』不過歡迎各路好手參與燒腦
只要限定從『X億X仟萬X佰萬X拾萬X萬X仟X佰X拾X』開始處理中文轉換就行了
/images/emoticon/emoticon41.gif
感謝參與回答的各位邦友/images/emoticon/emoticon41.gif

淺水員 iT邦研究生 4 級 ‧ 2020-06-27 00:29:20 檢舉
有自己寫的程式碼嗎?
我的解法詳見於 https://ithelp.ithome.com.tw/articles/10232114
可以直接copy GetAnswer(int val) 與 ConvertLen2Dec(string str) 從
從字串『X億X仟萬X佰萬X拾萬X萬X仟X佰X拾X』開始處理中文轉換
不過樓下的fysh711426大大, 也把那幾步都整合進Linq了
1
最佳解答

處理『億、萬』的地方有點亂。 /images/emoticon/emoticon16.gif

線上測試


2020/06/27 更新

根據 淺水員大 提供的範例,上面的寫法有 Bug

20,0000,1000 -> 貳拾億萬零壹仟

多了一個萬。 (╯‵□′)╯︵┴─┴

只靠 Aggregate 沒辦法處理整組為零的情況 /images/emoticon/emoticon06.gif


目前網路上的規則有點亂
我大致分為三類

1. 遇到零一律補零

2,0010,1000 -> 貳億零壹拾萬『零』壹仟
2,0000,1000 -> 貳億『零』壹仟

2. 以群組為單位補零

2,0010,1000 -> 貳億零壹拾萬『』壹仟
2,0000,1000 -> 貳億『零』壹仟

3. 以群組為單位補零,不過如果整個群組都為零,則忽略

2,0010,1000 -> 貳億零壹拾萬『』壹仟
2,0000,1000 -> 貳億『』壹仟

網路上的轉換器普遍使用前兩種規則
Excel 的格式化則使用第三種

上面的程式我是使用規則一來寫
不過看起來應該是 淺水員大 提供的最正規
所以接下來就改用規則二


規則

  1. 每四個數字分為一組
  2. 重複的零只算一次
  3. 忽略每組末位的零
-> 200101000
-> 2,0010,1000
-> 貳 |零壹拾 |壹仟
-> 貳億|零壹拾萬|壹仟
-> 貳億零壹拾萬壹仟
-> 200001000
-> 2,0000,1000
-> 貳 |零|壹仟
-> 貳億|零|壹仟
-> 貳億零壹仟
-> 2000000000100
-> 2,0000,0000,0100
-> 貳 | x | x |零壹佰
-> 貳兆| x | x |零壹佰
-> 貳兆零壹佰

程式

public static string FormatChineseNumber(long number)
{
    var numberText = new List<string>
    {
        "零", "壹", "貳", "參", "肆", "伍", "陸", "柒", "捌", "玖"
    };

    var unitText = new List<string>
    {
        "", "拾", "佰", "仟"
    };

    var groupText = new List<string>
    {
        "", "萬", "億", "兆"
    };

    var result = number.ToString()
        // 反轉
        .Reverse()
        // 轉中文: 10 -> 壹零
        .Select(it => numberText[it - '0'])
        // 分組並加上單位: 壹零壹 -> 壹『佰』零壹
        .Select((it, i) => new
        {
            group = i / 4,
            text = it + (it == "零" ? "" : unitText[i % 4])
        })
        // 轉回來
        .Reverse()
        // 分組處理『億、萬』
        .GroupBy(it => it.group)
        .Aggregate(((bool prevGroupZero, string text))(false, ""),
            (r, it) =>
            {
                var temp = it
                    .Aggregate(((bool prevZero, string text))(false, ""),
                    (rr, itt) =>
                    {
                        if (itt.text == "零")
                            return (true, rr.text);
                            
                        // 將非零項目加入字串
                        return (false, rr.text + 
                            (rr.prevZero ? "零" : "") + itt.text);
                    });

                // 處理群組
                if (temp.text == "")
                    return (true, r.text );
                    
                // 將非零群組加入字串
                return (false, r.text + (r.prevGroupZero &&
                    !temp.text.StartsWith("零") ? "零" : "") + 
                    temp.text + groupText[it.Key]);
            });

    return result.text == "" ? "零" : result.text;
}

線上測試


邏輯有參考 淺水員大 的程式
寫完後覺得順很多,原來處理億萬的地方不太順

/images/emoticon/emoticon12.gif

2
froce
iT邦大師 1 級 ‧ 2020-06-27 01:56:44
units1 = ["", "拾", "百", "千"]
units2 = ["", "萬", "億"]
digitals = ["零", "壹", "貳", "參", "肆", "伍", "陸", "柒", "捌", "玖"]

test1 = 100
test2 = 60058905
test3 = 999999999

def digi2chinese(s):
	# 轉換數字成國字,並且補單位(個十百千)
	tmp = [
		digitals[int(c)] + units1[len(s) -1 - i] if c != "0" \
		else digitals[int(c)] \
		for i, c in enumerate(s)]
	
	# 合併重複之"零"
	tmp2 = ""
	for t in tmp:
		if tmp2 == "" or tmp2[-1] != "零" or t != "零":
			tmp2 += t
	
	# 處理尾部之零
	return tmp2[:-1] if tmp2[-1] == "零" else tmp2

def convert2chinese(num):
	# 將數字轉換為字串,並將其顛倒
	reversedStr = str(num)[::-1]
	
    # 將數字每4個分為一組
	tmp = [
		reversedStr[i:i+4] if i is not None \
		else reversedStr[i:] \
		for i in range(0, len(reversedStr), 4)]
	
    # 還原數字,並對其做轉換成正楷和加入單位(個十百千)
	tmp = [digi2chinese(t[::-1]) for t in tmp][::-1]
	
    # 補上大單位(萬億)並結合,高興的話還可以擴展
	return "".join(t+units2[len(tmp)-1-i] for i, t in enumerate(tmp))
	

print(convert2chinese(test1))
print(convert2chinese(test2))
print(convert2chinese(test3))

應該你愛擴充到多少位都可以


改進一些寫法,弄簡潔一點並補註解。
然後看了一下上面的,解法差不多嘛。哈

/images/emoticon/emoticon34.gif

1
淺水員
iT邦研究生 4 級 ‧ 2020-06-27 16:04:39

對於零的處理,如果依照國小教的念法,下列提供幾筆測試資料
20,1000,0000 => 二十億一千萬
20,0100,0000 => 二十億零一百萬
20,0000,1000 => 二十億零一千
20,0000,0100 => 二十億零一百

參考:
國小數學教學疑難問題與解決策略示例8
康軒國小報第八期

下面是 javascript 版本的程式碼
(沒用特殊的功能,應該很容易轉換成其他語言)

function toChineseNumber(s) {
    const uintA = ['', '萬', '億', '兆'],
        unitB = ['', '拾', '佰', '仟'],
        unitC = ['零', '壹', '貳', '參', '肆', '伍', '陸', '柒', '捌', '玖'];
    let outStr = '', //儲存輸出字串
        i = (s.length - 1) >>> 2, //uintA 的 index
        j = (s.length - 1) & 3, //unitB 的 index
        k = 0, //讀取 s 的 index
        zero = false, //前面是不是有零
        emptyBlock = true; //對於 uintA 區間,是不是四個位數都是零
    for (; i >= 0; --i) {
        for (; j >= 0; --j) {
            let x = parseInt(s[k++], 10);
            if (x === 0) {
                zero = true;
            } else {
                outStr += (zero ? '零' : '') + unitC[x] + unitB[j];
                zero = false;
                emptyBlock = false;
            }
        }
        j = 3;
        if (!emptyBlock) {
            outStr += uintA[i];
            emptyBlock = true;
            zero = false;
        }
    }
    return outStr;
}
看更多先前的回應...收起先前的回應...

有興趣可試試linqjs
雖然我沒玩過/images/emoticon/emoticon25.gif

淺水員 iT邦研究生 4 級 ‧ 2020-06-27 21:44:18 檢舉

其實我不是很了解你的目的主要是要「處理數字轉中文顯示」還是「透過這個問題來玩玩LinQ的花招」,我是針對前者回答的。
如果只是單純解題,在有辦法直接輸出的前提下,我會盡量避免使用產生半成品的字串後,再檢查字串重複來修改出正確答案的方式,畢竟這樣做還是比較沒效率的。

應該是說現在很多語言都漸漸有導入『Language Integrated Query,顧名思義就是此程式語言擁有查詢資料的能力』。當用這類語言編程時,可以增加一些可讀性。講白了,我寫Java web,『froce大』寫Python,『小碼農米爾 Mir大』寫.Net,不過我們都懂『SQL』。這個提問比較偏向『玩花招』沒錯。

不過還是感謝你的參與回答/images/emoticon/emoticon41.gif

我要發表回答

立即登入回答