首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

C# 實作 Big5 繁體轉 GB2312 簡體:呼叫 Kernel32 LCMapString 完整方案

     編輯:關於C#

在 .NET 早期版本(.NET Framework 2.0/3.5)中,內建編碼類別 Encoding.GetEncoding("big5") 僅負責位元組↔字元轉換,不具備繁簡字形轉換功能。若需將 Big5 編碼的繁體中文轉為 GB2312 編碼的簡體中文,必須呼叫 Windows API LCMapString(透過 kernel32.dll)配合 LCMAP_SIMPLIFIED_CHINESE 標誌。本文提供完整可運行的主控台程式範例,支援批次轉換目錄下符合條件的檔案。

核心原理

  1. 以 Big5 編碼讀取檔案內容為字串。
  2. 將字串轉為 GB2312 (Code Page 936) 位元組陣列 —— 此步驟僅改變編碼,字形仍為繁體。
  3. 呼叫 LCMapString,傳入 LOCALE_SYSTEM_DEFAULT (0x0804) 與 LCMAP_SIMPLIFIED_CHINESE (0x02000000),將位元組陣列中的繁體字形對應轉換為簡體字形。
  4. 以 GB2312 編碼將轉換後的位元組陣列寫入新檔案。

完整程式碼

using System;
using System.IO;
using System.Text;
using System.Runtime.InteropServices;

public class Big5ToGb2312Converter
{
    // LCMapStringA: ANSI 版本,配合 byte[] 使用
    [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true, EntryPoint = "LCMapStringA")]
    private static extern int LCMapString(
        int Locale,
        int dwMapFlags,
        byte[] lpSrcStr,
        int cchSrc,
        byte[] lpDestStr,
        int cchDest);

    private const int LOCALE_SYSTEM_DEFAULT = 0x0804;      // 系統預設地區 (中文環境)
    private const int LCMAP_SIMPLIFIED_CHINESE = 0x02000000; // 繁體→簡體
    private const int LCMAP_TRADITIONAL_CHINESE = 0x04000000; // 簡體→繁體 (反向)

    public static void Main(string[] args)
    {
        if (args.Length == 0)
        {
            Console.WriteLine("用法: Big5ToGb2312Converter.exe <來源目錄> [檔案篩選模式, 預設 *.*]");
            Console.WriteLine("範例: Big5ToGb2312Converter.exe D:\\Data *.txt");
            return;
        }

        string sourceDir = args[0];
        string pattern = args.Length > 1 ? args[1] : "*.*";

        if (!Directory.Exists(sourceDir))
        {
            Console.WriteLine($"目錄不存在: {sourceDir}");
            return;
        }

        string outputDir = Path.Combine(sourceDir, "conv");
        if (!Directory.Exists(outputDir))
            Directory.CreateDirectory(outputDir);

        string[] files = Directory.GetFiles(sourceDir, pattern);
        Console.WriteLine($"找到 {files.Length} 個檔案,開始轉換...");

        int success = 0, fail = 0;
        foreach (string file in files)
        {
            try
            {
                // 1. 以 Big5 讀取
                string content;
                using (var sr = new StreamReader(file, Encoding.GetEncoding("big5")))
                    content = sr.ReadToEnd();

                // 2. 繁體(Big5字形) → 簡體(GB2312字形)
                string converted = ConvertTraditionalToSimplified(content);

                // 3. 以 GB2312 寫入
                string destFile = Path.Combine(outputDir, Path.GetFileName(file));
                using (var sw = new StreamWriter(destFile, false, Encoding.GetEncoding("gb2312")))
                    sw.Write(converted);

                Console.WriteLine($"[OK] {Path.GetFileName(file)}");
                success++;
            }
            catch (Exception ex)
            {
                Console.WriteLine($"[失敗] {Path.GetFileName(file)}: {ex.Message}");
                fail++;
            }
        }

        Console.WriteLine($"\n完成: 成功 {success}, 失敗 {fail}. 輸出目錄: {outputDir}");
    }

    /// 
    /// 核心轉換邏輯: 字串(繁體字形) -> GB2312位元組 -> LCMapString轉簡體字形 -> 字串
    /// 
    private static string ConvertTraditionalToSimplified(string input)
    {
        // 取得 GB2312 編碼器 (Code Page 936)
        Encoding gb2312 = Encoding.GetEncoding(936);

        // 字串 -> GB2312 位元組 (此時字形仍為繁體)
        byte[] srcBytes = gb2312.GetBytes(input);
        byte[] destBytes = new byte[srcBytes.Length]; // 簡體字形通常不超過原長度

        // 呼叫 Windows API 轉換字形
        // cchSrc = -1 表示以 null 結尾字串處理, 但這裡傳 byte[] 需給實際長度
        int result = LCMapString(
            LOCALE_SYSTEM_DEFAULT,
            LCMAP_SIMPLIFIED_CHINESE,
            srcBytes,
            srcBytes.Length,
            destBytes,
            destBytes.Length);

        if (result == 0)
        {
            int err = Marshal.GetLastWin32Error();
            throw new System.ComponentModel.Win32Exception(err, "LCMapString 呼叫失敗");
        }

        // 位元組 -> 字串 (GB2312 解碼, 字形已為簡體)
        return gb2312.GetString(destBytes, 0, result); // 使用實際寫入長度
    }
}

關鍵細節補充

  • Encoding.GetEncoding(936) 等同 Encoding.GetEncoding("gb2312"),但用數字代碼頁更明確、效能略高。
  • LCMapStringA vs LCMapStringW:範例使用 LCMapStringA (ANSI) 搭配 byte[];若改用 LCMapStringW (Unicode) 需傳 char[] 並設 CharSet = CharSet.Unicode,邏輯相同。
  • 緩衝區大小:目的緩衝區長度設為來源長度即可,因繁簡轉換通常 1:1,極少膨脹。API 返回實際寫入字元/位元組數。
  • 錯誤處理:SetLastError = true 並檢查返回值為 0 時透過 Marshal.GetLastWin32Error() 取得錯誤碼。
  • .NET Core / .NET 5+ 注意:Encoding.GetEncoding(936) 需註冊 EncodingProvider(System.Text.Encoding.CodePages 套件),否則會擲回 NotSupportedException。

現代替代方案(.NET Core 3.0 / .NET 5+)

若專案已升級至 .NET Core 3.0 或更新版本,強烈建議改用內建的 System.Text.Encoding 搭配 ChineseConverter (NuGet: Microsoft.ML.Tokenizers 或第三方庫) 或直接使用 System.Globalization.CultureInfo 相關功能,避免 P/Invoke 部署困擾。但若必須維護 .NET Framework 老專案,上述 LCMapString 方案仍是最輕量、零依賴的選擇。

總結

透過 kernel32.dll::LCMapString 配合 LCMAP_SIMPLIFIED_CHINESE 標誌,我們能在不引入額外第三方庫的前提下,可靠地完成 Big5 繁體到 GB2312 簡體的「編碼+字形」雙重轉換。範例程式已包含完整錯誤處理、批次處理與編碼正確性保障,可直接投入生產環境使用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved