改用VS2015後沒多久就發現它處理BIG5(ANSI)編碼程式碼的原則不同於以往(推測與編譯器改用Roslyn有關),導致部分使用BIG5編碼存檔的古老程式檔,會因許功蓋造成編譯錯誤。
PO文隔兩天同事跟我說,他們換VS2015後也射了好一陣子茶包,最後爬文又爬回我的文章。XD 後來聊到可以寫程式把所有BIG5編碼程式檔轉成UTF8一勞永逸,同事說檔案沒幾個,手動另存就搞定了,還不需要養乳牛。
這兩天,收到網友留言詢問VS2015是否會修正這個問題;也有網友提到手上專案有成千上萬個cs,改了一個BIG5,還有千千萬萬個BIG5,只好跟VS2015說Goodbye。
首先,雖然不確定VS2015會不會針對此一Issue進行修正,但依我之見,BIG5編碼已經過時多年,除了在VS2015產生不相容,遇到中文難字及其他語系文字都得額外處理(在Visual Studio.NET 2003時代就有處理過)。因此,不管VS2015會不會修正,將程式碼統一改存UTF8編碼是正確的方向。
問題來了,如網友所說,若專案有上萬支cs,一一手動轉存UTF8的浩大工程確實令人心寒。我最愛打造這種可以省時省力的潛盾機,就寫支批次轉檔程式搞定吧!
這裡我假設專案的.cs檔案分成兩類,一部分已經是UTF8或Unicode編碼,其餘則為BIG5編碼(假設全部都是BIG5,排除摻雜簡體中文、日文等其他ANSI編碼的情況)。因此,可以用Directory.GetFiles()搜尋找出特定目錄(含子目錄)下的指定檔案型別(例如:cs及js),檢查檔案註記略過UTF8或Unicode編碼檔案,找到BIG5檔案後先用BIG5 Encoding讀取,再以UTF8 Encoding寫入就完成轉檔,而原來的檔案則加上.big5.bak副檔名備份保留。
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace B2UBatchConverter { class Program { publicclass AnalyzeResult { publicstring Content; public Encoding Encoding; } //REF:http://goo.gl/jAJgIr by Rick Strahl publicstatic AnalyzeResult AnalyzeFile(string srcFile) { //預設為Big5 Encoding enc = Encoding.GetEncoding(950); //由前五碼識別出UTF8、Unicode、UTF32等編碼,其餘則視為 byte[] buffer = newbyte[5]; using (FileStream file = new FileStream(srcFile, FileMode.Open)) { file.Read(buffer, 0, 5); file.Close(); if (buffer[0] == 0xef && buffer[1] == 0xbb && buffer[2] == 0xbf) enc = Encoding.UTF8; elseif (buffer[0] == 0xfe && buffer[1] == 0xff) enc = Encoding.Unicode; elseif (buffer[0] == 0 && buffer[1] == 0 && buffer[2] == 0xfe && buffer[3] == 0xff) enc = Encoding.UTF32; elseif (buffer[0] == 0x2b && buffer[1] == 0x2f && buffer[2] == 0x76) enc = Encoding.UTF7; } //使用指定的Encoding讀取內容 returnnew AnalyzeResult() { Content = File.ReadAllText(srcFile, enc), Encoding = enc }; } staticvoid Main(string[] args) { //args = new string[] { "D:\\Lab\\L805\\ConApp" }; string path = args[0]; //列舉要搜尋轉碼的副檔名 var scanFileTypes = "cs,js".Split(','); //略過不處理的資料夾名稱 var skipFolders = "bin,obj".Split(','); foreach (var file in //列舉所有子目錄下的檔案 Directory.GetFiles(path, "*.*", SearchOption.AllDirectories)) { //取得副檔名 var ext = Path.GetExtension(file).TrimStart('.').ToLower(); //若非預先指定的副檔名就略過不處理 if (!scanFileTypes.Contains(ext)) continue; //處於\bin\* \obj\*目錄下的檔案也一律略過 if (skipFolders.Any(o => file.Contains( Path.DirectorySeparatorChar + o + Path.DirectorySeparatorChar))) continue; //讀取檔案內容並識別編碼 var analysis = AnalyzeFile(file); if (analysis.Encoding.CodePage == 950) //BIG5編號檔案才要處理 { Console.Write("Process File {0}...", file); //將原檔更名為*.big5.bak var bakFile = file + ".big5.bak"; if (File.Exists(bakFile)) File.Delete(bakFile); File.Move(file, bakFile); //重新以UTF8寫入 File.WriteAllText(file, analysis.Content, Encoding.UTF8); Console.WriteLine(" done!"); } else { Console.WriteLine("Skip File {0} / {1}", file, analysis.Encoding.EncodingName); } } } } } |
專案編譯成Console Application,執行時提供路徑名稱作為參數,轉換程式就會掃瞄該目錄下所有的.cs及.js,一口氣將BIG5編碼程式碼轉存成UTF8。
執行範例如下,原本BIG5編碼的B5Class.cs另存成B5Class.cs.big5.bak,而B5Class.cs已改為UTF8編碼,就算有成千上萬支程式要轉碼也不怕。
希望這個工具能解決一些朋友的困擾。
【提醒】批次轉換前請務必先備份,以避免轉換出錯造成資料遺失。
【2015-08-07 更新】
感謝網友黃文生、Norton Lin在FB專頁留這補充兩款現成軟體工具:ConvertZ以及UTFCast也可以做到批次編碼轉換。
另外,ChrisTorng 也分享可與TFS整合的批次轉檔,並提到靠 StreamReader 建構式之 detectEncodingFromByteOrderMarks 參數偵測Encoding的小技巧。
謝謝大家的回饋。