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
{
staticvoid 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
staticstring toNCR(string input)
{
StringBuilder sb = new StringBuilder();
Encoding big5 = Encoding.GetEncoding("big5");
foreach (char c in input)
{
//強迫轉碼成Big5,看會不會變成問號
string cInBig5 = big5.GetString(big5.GetBytes(newchar[] {c}));
//原來不是問號,轉碼後變問號,判定為難字
if (c!='?'&& cInBig5=="?")
sb.AppendFormat("&#{0};", Convert.ToInt32(c));
else
sb.Append(c);
}
return sb.ToString();
}
staticstring fromNCR(string input)
{
return Regex.Replace(input, "&#(?<ncr>\\d+?);", (m) =>
{
return Convert.ToChar(int.Parse(m.Groups["ncr"].Value)).ToString();
});
}
}
}