Microsoft.VisualBasic.Strings.StrConv靜態方法是在.NET轉換半形全形最簡便的做法(即使語言是C#也沒差,在專案加入Microsoft.VisualBasic參照即可),最近實際用在專案,又發現了一些眉角,整理筆記備忘:

  1. 在開發機測試OK,丟到測試機執行冒出VbStrConv.Wide and VbStrConv.Narrow are not applicable to the locale specified.錯誤。研究後發現問題出在測試機作業系統語系為英文,轉換中文字串會因語系不符出錯。解決方法為傳入第三個參數指定LCID(未傳入的話會依作業系統語系決定),例如:
    Strings.StrConv("黑暗執行緒ABC123", VbStrConv.Narrow, 1028)
    繁體中文的LCID是1028、簡體中文則為2052,詳細列表可參考維基百科
  2. 因為要指定語系LCID,故務必確認輸入字串的語系,否則部分字元會變成問號,例如:
    Strings.StrConv("黑暗执行绪ABC123(简体)", VbStrConv.Narrow, 1028)
    簡體字串誤設1028(台灣繁體中文)會變成"黑暗?行?ABC123(?体)",需改為2052才會正確傳回"黑暗执行绪ABC123(简体)" 。
  3. 要指定語系?聰明如你,可能已經想到「如果有Big5難字怎麼辦?」 例如:"黑暗執行緒ABC123"。
    不幸地,Strings.StrConv全形半形轉換得靠ANSI編碼(例如:BIG5)將字元轉成位元組進行判斷,不支援Unicode。以上例子為繁體中文需指定1028,傳回結果「犇」會變成問號"黑暗執行緒?ABC123"。
    放棄Strings.StrConv,自己寫全形半形製轉換函式是一種解法。懶惰如我,則選擇用雞嗚狗盗之術抄小路,參考以前寫的潛盾機,先將難字轉成NCR(&#29319),其餘字元保留,做完全形半形轉換再將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

謝謝黑大,用到了,感謝。

Post a comment