Strings.StrConv半形全形轉換注意事項三則
| | | 2 | |
Microsoft.VisualBasic.Strings.StrConv靜態方法是在.NET轉換半形全形最簡便的做法(即使語言是C#也沒差,在專案加入Microsoft.VisualBasic參照即可),最近實際用在專案,又發現了一些眉角,整理筆記備忘:
- 在開發機測試OK,丟到測試機執行冒出VbStrConv.Wide and VbStrConv.Narrow are not applicable to the locale specified.錯誤。研究後發現問題出在測試機作業系統語系為英文,轉換中文字串會因語系不符出錯。解決方法為傳入第三個參數指定LCID(未傳入的話會依作業系統語系決定),例如:
Strings.StrConv("黑暗執行緒ABC123", VbStrConv.Narrow, 1028)
繁體中文的LCID是1028、簡體中文則為2052,詳細列表可參考維基百科。 - 因為要指定語系LCID,故務必確認輸入字串的語系,否則部分字元會變成問號,例如:
Strings.StrConv("黑暗执行绪ABC123(简体)", VbStrConv.Narrow, 1028)
簡體字串誤設1028(台灣繁體中文)會變成"黑暗?行?ABC123(?体)",需改為2052才會正確傳回"黑暗执行绪ABC123(简体)" 。 - 要指定語系?聰明如你,可能已經想到「如果有Big5難字怎麼辦?」 例如:"黑暗執行緒犇ABC123"。
不幸地,Strings.StrConv全形半形轉換得靠ANSI編碼(例如:BIG5)將字元轉成位元組進行判斷,不支援Unicode。以上例子為繁體中文需指定1028,傳回結果「犇」會變成問號"黑暗執行緒?ABC123"。
放棄Strings.StrConv,自己寫全形半形製轉換函式是一種解法。懶惰如我,則選擇用雞嗚狗盗之術抄小路,參考以前寫的潛盾機,先將難字轉成NCR(犇),其餘字元保留,做完全形半形轉換再將NCR還原回Unicode難字,薑!薑!薑!薑!搞定囉~ (細節請參考程式範例)
註:此處未考慮原本字串夾帶NCR的情境,且未細究效能,如要應用請自行改寫、評估。
展示程式如下供參:
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Web; namespace ConsoleApplication1 { class Program { static void Main(string[] args)
{ //測試1 Debug.WriteLine(Microsoft.VisualBasic.Strings.StrConv(
"黑暗執行緒ABC123", Microsoft.VisualBasic.VbStrConv.Narrow) );
//結果:黑暗執行緒ABC123 //測試2 //切成英語語系(原本為zh-TW) Thread.CurrentThread.CurrentCulture =
new System.Globalization.CultureInfo("en-US");
try { Debug.WriteLine(Microsoft.VisualBasic.Strings.StrConv(
"黑暗執行緒ABC123", Microsoft.VisualBasic.VbStrConv.Narrow)); }
catch (Exception ex) { Debug.WriteLine("錯誤:" + ex.Message); //結果: // 錯誤:VbStrConv.Wide and VbStrConv.Narrow are not applicable to the locale specified. }
//測試3 加上LCID參數 Debug.WriteLine(Microsoft.VisualBasic.Strings.StrConv(
"黑暗執行緒ABC123", Microsoft.VisualBasic.VbStrConv.Narrow, 1028)); //結果:黑暗執行緒ABC123 //測試4 簡體中文套用繁體中文LCID會產生問號 Debug.WriteLine(Microsoft.VisualBasic.Strings.StrConv(
"黑暗执行绪ABC123(简体)", Microsoft.VisualBasic.VbStrConv.Narrow, 1028)); //結果:黑暗?行?ABC123(?体) //測試5 套用簡體中文LCID,結果正確 Debug.WriteLine(Microsoft.VisualBasic.Strings.StrConv(
"黑暗执行绪ABC123(简体)", Microsoft.VisualBasic.VbStrConv.Narrow, 2052)); //結果:黑暗执行绪ABC123(简体) //測試6 夾雜Big5難字 Debug.WriteLine(Microsoft.VisualBasic.Strings.StrConv(
"黑暗執行緒犇ABC123", Microsoft.VisualBasic.VbStrConv.Narrow, 1028)); //結果:黑暗執行緒?ABC123 //測試7 黑暗小雜技:先將難字換成NCR,轉完半形再換回來 var ncrString = toNCR("黑暗執行緒犇ABC123"); Debug.WriteLine(ncrString); //黑暗執行緒犇ABC123 var convString = Microsoft.VisualBasic.Strings.StrConv(
ncrString, Microsoft.VisualBasic.VbStrConv.Narrow, 1028);
Debug.WriteLine(convString); //黑暗執行緒犇ABC123 var resultString = fromNCR(convString);
Debug.WriteLine(resultString); //黑暗執行緒犇ABC123 Console.Read();
}
//REF: http://blog.darkthread.net/blogs/darkthreadtw/archive/2007/04/21/733.aspx static string toNCR(string input)
{ StringBuilder sb = new StringBuilder(); Encoding big5 = Encoding.GetEncoding("big5"); foreach (char c in input)
{ //強迫轉碼成Big5,看會不會變成問號 string cInBig5 = big5.GetString(big5.GetBytes(new char[] {c}));
//原來不是問號,轉碼後變問號,判定為難字 if (c!='?' && cInBig5=="?")
sb.AppendFormat("&#{0};", Convert.ToInt32(c)); else sb.Append(c);
}
return sb.ToString(); }
static string fromNCR(string input)
{ return Regex.Replace(input, "&#(?<ncr>\\d+?);", (m) =>
{ return Convert.ToChar(int.Parse(m.Groups["ncr"].Value)).ToString();
});
}
}
}
Comments
# by wellxion
VB的是好用... 可惜難字是個問題...www 後來是參考這個土法煉鋼:D http://www.dotblogs.com.tw/shunnien/archive/2013/07/21/111737.aspx
# by Eric Huang
謝謝黑大,用到了,感謝。