CODE-將地址中的阿拉伯數字轉為中文大寫

前幾天同事討論到要將地址資料中的阿拉伯數字都轉成中文大寫(一二三四...),我想起了前些時候看到的Microsoft Visual Studio International Feature Pack 2.0就內建了數字轉中文大寫的功能,試作如下。

程式主要是用Regex去比對出數字(\d+)的部分,逐一換成中文大寫。而更換時我用算位置的方法而不直接用Replace,以免把"12弄123號"搞成"十二弄十二3號";也因為要算位置,加上每次更換完字串長度可能會改變,所以也不能直接用foreach (Match m in Regex.Matches(…))把所有數字挑出來一次處理,必須用while (Match.Success)個個擊破。[2009-12-23更新: 以下寫法不夠精簡,另外有三百"一"十少一的問題,請改參考下方的改良版]

using System;
using System.Text.RegularExpressions;
using Microsoft.International.Formatters;
using System.Globalization;
 
namespace TestChtNumber
{
    class Program
    {
        static void Main(string[] args)
        {
            string addr = "台北市中正區重慶南路1段122號";
            Match m;
            while ((m=Regex.Match(addr, "\\d+")).Success)
            {
                int n = int.Parse(m.Value);
                string t =
                    EastAsiaNumericFormatter.FormatWithCulture("Ln", n,
                    null, new CultureInfo("zh-TW"));
                //"L"-大寫,壹貳參... "Ln"-一二三... "Lc"-貨幣,同L
                addr = addr.Substring(0, m.Index) + t +
                       addr.Substring(m.Index + m.Value.Length);
            }
            Console.WriteLine(addr);
            Console.Read();
        }
    }
}

得到結果: 台北市中正區重慶南路一段一百二十二號

很好用吧!! 另外,元件也支援簡繁轉換的功能,是另一項天上掉下來的禮物,不要錯過了。

【延伸閱讀】

【更新2009/12/23】接獲線報,這元件有個Bug,"310"會被轉成"三百十",而我們口語會說"三百一十",好好的美人兒偏偏要長一顆痣,實在是... (氣) 晚點再來想要如何整容。

【更新2009/12/23】感謝Dino與Phoenix的補充,讓我這條老狗又學到新把戲了(MatchEvaluator是好物呀!),以下我想到為解決三百"一"十問題的修正版,歡迎大家批評指教!

【更新2015/04/12】感謝YinChang補充,再加入「拾萬」要轉為「壹拾萬」之邏輯。


using System;
using System.Text.RegularExpressions;
using Microsoft.International.Formatters;
using System.Globalization;
 
namespace TestNumber
{
    class Program
    {
        static void Main(string[] args)
        {
            for (int i = 0; i < 2000; i++)
            {
                Console.Write(FormatChineseNumber(i, false) + " ");
            }
            Console.WriteLine(FormatChineseNumber(
                string.Format("中華民國{0}年{1}月{2}日",
                DateTime.Today.Year - 1911,
                DateTime.Today.Month,
                DateTime.Today.Day), false));
            Console.WriteLine(FormatChineseNumber(12.345M, false));
            Console.WriteLine(FormatChineseNumber(3100000M, true));
            Console.WriteLine(FormatChineseNumber(100000M, true));
            Console.Read();
        }
 
        /// <summary>
        /// 將字串的數字部分轉為中文大寫
        /// </summary>
        /// <param name="s">原始字串</param>
        /// <param name="moneyChar">
        /// 是否使用金額大寫,true時使用"壹貳參肆...", false時則為"一二三四..."
        /// </param>
        /// <returns>轉換後字串</returns>
        static string FormatChineseNumber(string s, bool moneyChar)
        {
            return Regex.Replace(s, "\\d+", m =>
            {
                int n = int.Parse(m.Value);
                return FormatChineseNumber(n, moneyChar);
            });
        }
 
        /// <summary>
        /// 修正EastAsiaNumericFormatter.FormatWithCulture出現"三百十"之問題,
        /// 本函數會將其修正為三百一十的慣用寫法
        /// 2015-04-12更新,增加拾萬改為壹拾萬邏輯
        /// </summary>
        /// <param name="n">要轉換的數字</param>
        /// <param name="moneyChar">
        /// 是否使用金額大寫,true時使用"壹貳參肆...", false時則為"一二三四..."
        /// </param>
        /// <returns>轉為中文大寫的數字</returns>
        static string FormatChineseNumber(decimal n, bool moneyChar)
        {
            //"L"-大寫,壹貳參... "Ln"-一二三... "Lc"-貨幣,同L
            string t =
                EastAsiaNumericFormatter.FormatWithCulture(
                moneyChar ? "L" : "Ln", n,
                null, new CultureInfo("zh-TW"));
            string pattern = moneyChar ?
                    "[^壹貳參肆伍陸柒捌玖]拾" :
                    "[^一二三四五六七八九]十";
            string one = moneyChar ? "壹" : "一";
            string res = Regex.Replace(t, pattern, m =>
            {
                return m.Value.Substring(0, 1) + one +
                    m.Value.Substring(1);
            });
            //拾萬需補為壹拾萬
            if (moneyChar && res.StartsWith("拾"))
            {
                res = "壹" + res;
            }
            return res;
        }
    }
}

歡迎推文分享:
Published 23 December 2009 05:25 AM 由 Jeffrey
Filed under: , ,
Views: 23,770



意見

# chuck said on 22 December, 2009 05:42 PM

原來還有這種東西喔 真好用EastAsiaNumericFormatter.FormatWithCulture

# Dino said on 22 December, 2009 06:46 PM

透過黑大理解到 Microsoft Visual Studio International Feature Pack 2.0 的功能了!

不過我覺得 Regex 的做法 這樣可能會更好:

--------------

string addr = "台北市中正區重慶南路1段122號";

string tran = Regex.Replace(addr, @"\d+", delegate(Match match)

{

 int n = int.Parse(match.Value);

 return EastAsiaNumericFormatter.FormatWithCulture("Ln", n, null, new CultureInfo("zh-TW"));

});

--------------

# Phoenix said on 22 December, 2009 07:25 PM

原來已經有人知道.Net Regex的秘密能力了@@

用Lambda Expression再簡化程式碼。

------------------------------------------

string addr = "台北市中正區重慶南路1段122號";

addr = Regex.Replace(addr, "\\d+", m => EastAsiaNumericFormatter.FormatWithCulture(

"Ln", int.Parse(m.Value), null, new CultureInfo("zh-TW")));

Console.WriteLine(addr);

Console.Read();

# Dino said on 22 December, 2009 07:31 PM

To Phoenix,

讚!

# Jeffrey said on 22 December, 2009 09:57 PM

to Dino, Phoenix, 感謝補充,我又學到新東西囉(灑花~~)。我在三百"一"十改良版中已融入MatchEvaluator+Lambda的做法,謝謝兩位!

# YinChang said on 10 April, 2015 07:01 AM

感謝分享關於所提的"310"會被轉成"三百十"的bug,

補充說明一下轉大寫貨幣要注意一下(剛好被客戶抓到),

如:"拾萬元整"正確是"壹拾萬元整"

應該是同一bug,需多判斷開頭為"拾"需在前頭補上"壹"

# Jeffrey said on 12 April, 2015 07:40 AM

to YinChang, 已在程式再加入「拾萬」補為「壹拾萬」邏輯,感謝你的補充讓程式更完整。

你的看法呢?

(必要的) 
(必要的) 
(選擇性的)
(必要的) 
(提醒: 因快取機制,您的留言幾分鐘後才會顯示在網站,請耐心稍候)

5 + 3 =

搜尋

Go

<December 2009>
SunMonTueWedThuFriSat
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789
 
RSS
創用 CC 授權條款
【廣告】
twMVC
最新回應

Tags 分類檢視
關於作者

一個醉心技術又酷愛分享的Coding魔人,十年的IT職場生涯,寫過系統、管過專案, 也帶過團隊,最後還是無怨無悔地選擇了技術鑽研這條路,近年來則以做一個"有為的中年人"自許。

文章典藏
其他功能

這個部落格


Syndication