2012年7月11日 星期三

C# 繁簡轉換效能大車拚


就目前已知在.Net平台上繁簡互轉的作法大約有四種
1. Microsoft.VisualBasic.dll 
    .Net平台內建可以直接參考使用,效率三級。                
2. Microsoft Visual Studio International Pack 1.0 SR1
    微軟官方所出的官方套件,需要另外安裝,目前不支援 VS 2008 以上的版本,官方網站下載後無法在VS 2008 以上的版本安裝,需要另外在網路上尋找 ChineseConverter.dll 加入參考,效率二級。               
3. Microsoft.Office.Interop.Word.dll(Office 2010 Ver.14.0.4762.1000)
    系統上若裝了 Office 就有,唯一提供繁簡詞意互轉的套件,但是轉換效能就不那麼漂亮,效率五級。               
4. OS Kernel LCMapString
    什麼都不用裝,直接使用系統內核kernel32.dll 提供的LCMapString 來進行轉換,效率一級。

測試結果數據如下︰ 
VisualBasic Convert︰15.9458 ms
Microsoft.International.Converters.TraditionalChineseToSimplifiedConverter︰3.5011 ms       
Microsoft.Office.Interop.Word︰10082.5081 ms
Kernel32 LCMapString︰1.5212 ms

相關的程式碼︰
///
/// 使用系統 kernel32.dll 進行轉換
///
private const int LocaleSystemDefault = 0x0800;
private const int LcmapSimplifiedChinese = 0x02000000;
private const int LcmapTraditionalChinese = 0x04000000;
 
[DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int LCMapString(int locale, int dwMapFlags, string lpSrcStr, int cchSrc,
                                      [Out] string lpDestStr, int cchDest);
 
public static string ToSimplified(string argSource)
{
    var t = new String(' ', argSource.Length);
    LCMapString(LocaleSystemDefault, LcmapSimplifiedChinese, argSource, argSource.Length, t, argSource.Length);
    return t;
}
 
public static string ToTraditional(string argSource)
{
    var t = new String(' ', argSource.Length);
    LCMapString(LocaleSystemDefault, LcmapTraditionalChinese, argSource, argSource.Length, t, argSource.Length);
    return t;
}
 
///
/// 使用 Office Word (Microsoft.Office.Interop.Word) 進行轉換
///
public static string ConvertUsingWord(string argSource, bool argIsCht)
{
    var doc = new Document();
    doc.Content.Text = argSource;
    doc.Content.TCSCConverter(
        argIsCht
            ? WdTCSCConverterDirection.wdTCSCConverterDirectionTCSC
            : WdTCSCConverterDirection.wdTCSCConverterDirectionSCTC, true, true);
    var ret = doc.Content.Text;
    object saveChanges = false;
    object originalFormat = Missing.Value;
    object routeDocument = Missing.Value;
    doc.Close(ref saveChanges, ref originalFormat, ref routeDocument);
    return ret;
}

測式碼︰
public void RunTest()
{   
    var i = 1000;
    var sw = new System.Diagnostics.Stopwatch();
    sw.Reset();
    sw.Start();
    while (--i > 0)
    {
        Strings.StrConv("她來聽我 的演唱會 在十七歲的初戀 第一次約會,繁轉簡", VbStrConv.SimplifiedChinese, 2052);
        Strings.StrConv("她来听我 的演唱会 在十七岁的初恋 第一次约会,簡轉繁", VbStrConv.TraditionalChinese, 2052);
    }
 
    sw.Stop();
    Response.Write(string.Format("VisualBasic Convert︰{0}",  sw.Elapsed.TotalMilliseconds.ToString(CultureInfo.InvariantCulture)));
    i = 1000;
    sw.Reset();
    sw.Start();
    while (--i > 0)
    {
        ChineseConverter.Convert("她來聽我 的演唱會 在十七歲的初戀 第一次約會,繁轉簡", ChineseConversionDirection.TraditionalToSimplified);
        ChineseConverter.Convert("她来听我 的演唱会 在十七岁的初恋 第一次约会,簡轉繁", ChineseConversionDirection.SimplifiedToTraditional);
    }
    sw.Stop();
    Response.Write(
        string.Format("Microsoft.International.Converters.TraditionalChineseToSimplifiedConverter︰{0}", sw.Elapsed.TotalMilliseconds.ToString(CultureInfo.InvariantCulture)));
 
    i = 100;
    sw.Reset();
    sw.Start();
    while (--i > 0)
    { 
        ConvertUsingWord("她來聽我 的演唱會 在十七歲的初戀 第一次約會,繁轉簡", true);
        ConvertUsingWord("她来听我 的演唱会 在十七岁的初恋 第一次约会,簡轉繁", false);
    }
    sw.Stop();
    Response.Write(string.Format("Microsoft.Office.Interop.Word︰{0}", sw.Elapsed.TotalMilliseconds.ToString(CultureInfo.InvariantCulture)));
    i = 1000;
    sw.Reset();
    sw.Start();
    while (--i > 0)
    { 
        ToSimplified("她來聽我 的演唱會 在十七歲的初戀 第一次約會,繁轉簡");
        ToTraditional("她来听我 的演唱会 在十七岁的初恋 第一次约会,簡轉繁");
    }
    sw.Stop();
    Response.Write(string.Format("kernel32 LCMapString︰{0}",sw.Elapsed.TotalMilliseconds.ToString(CultureInfo.InvariantCulture)));
}

9 則留言:

黃文風 提到...

VERY NICE ARTICLE, SAVE MY TIME

william 提到...

請問一下大大這個程式碼要寫在哪個資料夾裡呢?

Eddiea Chen 提到...

放在同一隻.cs檔就行了啊,直Call RunTest();

匿名 提到...

Great !
感謝..很有參考價值!

darkthread 提到...

感謝分享

Eddiea Chen 提到...

居然是黑暗大,小弟受寵若驚!!

Robin Li 提到...

多謝分享

林勁 提到...

太感謝了 QQ

www 提到...

感激不盡