CNS11643中文全字庫轉換Unicode注音字碼表之實作
因為有機會要用到中文輸入法的調用,查了半天,全部都是依存在Windows每一版本的作業系統中所提供的不同輸入法的DLL程式庫,再進行所謂的DLL Hook,因為不想自己寫的系統元件或程式,被Windows的作業系統版本綁住,所以乾脆把研究方向轉向到自己自建字碼表(在本篇以注音輸入法為例),這樣一來就可以完全的從作業系統底層解脫了。
當然啦,我們不可能自己建立這些字碼表,因此行政院國家發展委員會的全字庫自然是我這次所有研究的重點,再繼續挖掘下去,發現全字庫已經有在政府開放資料網站上進駐,也就是說,我們可以不用辛苦的到他的網站上寫程式慢慢的crawler了。網站在這裡:政府資料開放平台:CNS11643中文標準交換碼全字庫
將全字庫資料轉換成Unicode實體文字,以及對照的注音字碼表
有了全字庫的資料,再稍微地讀一下他的檔案結構,對於有程式設計基礎的人來說,轉換這些數值變成文字,應該都不是太難的事情,以下是我寫的程式碼範例。在轉換的過程中,先暫時捨棄Private Use Area-A,只保留基本多文種平面Basic Multilingual Plane以及表意文字補充平面Supplementary Ideographic Plane。
namespace Slashview
{
class Program
{
static void Main(string[] args)
{
string cBasePath = System.Environment.CurrentDirectory;
//OpenData CNS 主要檔案
string cCNS_PhoneticPath = "CNS_phonetic.txt";
//OpenData CNS 2 Unicode 對照檔案
string cCNS2UNICODE_Unicode_BMP = "CNS2UNICODE_Unicode BMP.txt"; //Basic Multilingual Plane(BMP)基本多文種平面
string cCNS2UNICODE_Unicode_2 = "CNS2UNICODE_Unicode 2.txt"; //Supplementary Ideographic Plane(SIP)表意文字補充平面
string cCNS2UNICODE_Unicode_15 = "CNS2UNICODE_Unicode 15.txt"; //Private Use Area-A(PUA-A)保留作為私人使用區(A區)
//檢查檔案是否存在
if (!System.IO.File.Exists($"{cBasePath}\\{cCNS_PhoneticPath}")) { WriteLine($"找不到檔案:{cCNS_PhoneticPath}"); return; }
if (!System.IO.File.Exists($"{cBasePath}\\{cCNS2UNICODE_Unicode_BMP}")) { WriteLine($"找不到檔案:{cCNS2UNICODE_Unicode_BMP}"); return; }
if (!System.IO.File.Exists($"{cBasePath}\\{cCNS2UNICODE_Unicode_2}")) { WriteLine($"找不到檔案:{cCNS2UNICODE_Unicode_2}"); return; }
if (!System.IO.File.Exists($"{cBasePath}\\{cCNS2UNICODE_Unicode_15}")) { WriteLine($"找不到檔案:{cCNS2UNICODE_Unicode_15}"); return; }
//讀取主要檔案
WriteLine("讀取主要檔案...");
System.Collections.Generic.List<TableCnsPhonetic> oMain = new System.Collections.Generic.List<TableCnsPhonetic>();
using (System.IO.StreamReader oSR = new System.IO.StreamReader($"{cBasePath}\\{cCNS_PhoneticPath}"))
{
string oLine;
while (!string.IsNullOrWhiteSpace(oLine = oSR.ReadLine()))
{
string[] oTemp = oLine.Split('\t');
oMain.Add(new TableCnsPhonetic {
cCNSCode = $"{oTemp[0]}-{oTemp[1]}",
cPhonetic = $"{oTemp[2]}"
});
}
}
//讀取字典檔
WriteLine("讀取字典檔案...");
System.Collections.Generic.List<TableCnsUnicode> oDictionary = new System.Collections.Generic.List<TableCnsUnicode>();
using (System.IO.StreamReader oSR = new System.IO.StreamReader($"{cBasePath}\\{cCNS2UNICODE_Unicode_2}"))
{
string oLine;
while (!string.IsNullOrWhiteSpace(oLine = oSR.ReadLine()))
{
string[] oTemp = oLine.Split('\t');
oDictionary.Add(new TableCnsUnicode
{
cCNSCode = $"{oTemp[0]}-{oTemp[1]}",
cUnicode = $"{oTemp[2]}"
});
}
}
using (System.IO.StreamReader oSR = new System.IO.StreamReader($"{cBasePath}\\{cCNS2UNICODE_Unicode_15}"))
{
string oLine;
while (!string.IsNullOrWhiteSpace(oLine = oSR.ReadLine()))
{
string[] oTemp = oLine.Split('\t');
oDictionary.Add(new TableCnsUnicode
{
cCNSCode = $"{oTemp[0]}-{oTemp[1]}",
cUnicode = $"{oTemp[2]}"
});
}
}
using (System.IO.StreamReader oSR = new System.IO.StreamReader($"{cBasePath}\\{cCNS2UNICODE_Unicode_BMP}"))
{
string oLine;
while (!string.IsNullOrWhiteSpace(oLine = oSR.ReadLine()))
{
string[] oTemp = oLine.Split('\t');
oDictionary.Add(new TableCnsUnicode
{
cCNSCode = $"{oTemp[0]}-{oTemp[1]}",
cUnicode = $"{oTemp[2]}"
});
}
}
//比對與儲存
WriteLine("比對主要檔案與字典檔案,並運算成資料庫...");
System.Collections.Generic.List<TableFinalResult> oResult = new System.Collections.Generic.List<TableFinalResult>();
int iCounter = 1;
foreach (var oTempS in oMain)
{
Console.CursorLeft = 0;
Write($"處理進度:{iCounter}/{oMain.Count}");
string cHexCode = "";
foreach (var oTempT in oDictionary) { if (oTempS.cCNSCode == oTempT.cCNSCode) { cHexCode = oTempT.cUnicode; break; }}
//將Unicode Private Use Area-A(PUA-A)過濾掉,因為一般使用者不可能會有這些字形檔
if (cHexCode.Length == 5 && cHexCode.Substring(0, 1) == "F") { cHexCode = ""; }
//比對後儲存
if (cHexCode.Length != 0)
{
int iTemp = System.Convert.ToInt32(cHexCode, 16);
oResult.Add(new TableFinalResult()
{
iUnicode = iTemp,
cUnicode = cHexCode,
cPhonetic = oTempS.cPhonetic,
cString = System.Text.Encoding.UTF32.GetString(System.BitConverter.GetBytes(iTemp))
});
}
iCounter++;
}
//寫成檔案
WriteLine("將資料庫寫成檔案...");
using (System.IO.StreamWriter oSW = new System.IO.StreamWriter($"{cBasePath}\\中文全字庫注音資料庫檔案.txt", false, System.Text.Encoding.UTF8))
{
foreach (var oTemp in oResult)
{
oSW.WriteLine($"{oTemp.iUnicode}\t{oTemp.cUnicode}\t{oTemp.cPhonetic}\t{oTemp.cString}");
}
}
}
}
public class TableCnsPhonetic
{
public string cCNSCode { get; set; }
public string cPhonetic { get; set; }
}
public class TableCnsUnicode
{
public string cCNSCode { get; set; }
public string cUnicode { get; set; }
}
public class TableFinalResult
{
public int iUnicode { get; set; }
public string cUnicode { get; set; }
public string cPhonetic { get; set; }
public string cString { get; set; }
}
}
經過轉換後,我們將會得到十進制(Dec)的Unicode編碼、十六進制(Hex)的Unicode編碼、注音字碼表、真實的Unicode文字,好歡樂啊!接下來就可以快樂的倒進資料庫中,快快樂樂的設計實作自己的輸入法嘍!