大家有遇到 git diff 比對文字檔,因中文編碼更改(例如 Big5 改 UTF-8)導致結果裡有一半中文變亂碼的情況嗎?我想到一個完美解法。(我自己覺得啦,不服來戰)

git diff 遇到文字檔中文編碼不同的問題之前處理過(參考:Git 實戰技巧 - 使用 git diff 比對 UTF-16/BIG5 文字檔),但定義 textconf 再用 .gitattributes 指定檔案套用編碼的做法稱不上完美,實務上有些無法處理的狀況,例如:

  1. git diff --no-index a.txt b.txt 任意比對非 Git Repository 下的檔案,就很難用 .gitattributes 指定中文編碼
  2. 同一個檔案由 Big5 換成 UTF-8 (翻修古蹟常會遇到),同一檔案修改前是 Big5,修改後是 UTF-8,在 .gitattributes 指定哪一種編碼都不對

為了觀察,我做了四個檔案,big5-src.txt、big5-dst.txt、utf8-src.txt、utf8-dst.txt,-src.txt 的內容為「中文測試 1234」、-dst.txt 的內容為「中文測試 ABC」,再分別用 Big5 或 UTF-8 編碼存檔。接著用以下批次檔排列組合存成四種結果:

git diff --no-index big5-src.txt big5-dst.txt > diff-b5-b5.txt
git diff --no-index big5-src.txt utf8-dst.txt > diff-b5-u8.txt
git diff --no-index utf8-src.txt utf8-dst.txt > diff-u8-u8.txt
git diff --no-index utf8-src.txt big5-dst.txt > diff-u8-b5.txt

如圖所示,git diff ... > result.txt 時,git 將保留檔案原始內容,Big5 就存 Big5,UTF-8 就存 UTF-8 (Grabage In Garbage Out),講求原汁原味,再看讀取檔案的程式如何解讀。故左側的 Big5 對 Big5 與 UTF-8 對 UTF-8 沒什麼爭議,Notepad++ 自動識別成 Big5 及 UTF-8。但右側兩個檔案同時有 Big5 及 UTF-8,若 Notepad++ 先看到 Big5 中文,檔案會被視為 Big5 (ANSI),UTF-8 部分變亂碼;若先看到 UTF-8 中文,則 Big5 部分變亂碼。

嘗試過幾個解決方向,最後我想到一個美妙解法 - 讓 git diff 結果維持原有 Big5 與 UTF-8 並存的狀況,寫一支工具程式將其解讀成 UTF-8,但是將 Big5 部分轉換成 UTF-8,並標示 <BIG5>,用這個美化版本輔助想釐清亂碼內容的人理解。

美化效果如下:(Cmder type 預設用 Big5 編碼,故 UTF-8 部分變亂碼,美化後 Big5 與 UTF-8 都能正確顯示)

美化程式還加了簡單的亂碼偵測,只在包含 Big5 中文時標註 <BIG5>,純英文時則正常輸出。

附上 BeautifyBig5Diff.ps1 程式碼,想用的同學請自取修改使用。

param (
    [Parameter(Mandatory=$true)]
    [string]$diffPath
)
$sourceCode = @"
using System.IO;
using System.Text;
using System;
public class DiffBig5Convertor {
    static Encoding encBig5 = Encoding.GetEncoding(950);
    static Encoding encUTF8 = Encoding.UTF8;
    public static string Convert(string diffPath) {
        var buff = File.ReadAllBytes(diffPath);
        var lineStartPos = 0;
        var inDiff = false;
        var sb = new StringBuilder();
        for (var idx = 0; idx < buff.Length; idx++) {
            if (buff[idx] == 0x0a || idx == buff.Length - 1) 
            {
                byte[] lineBytes = new byte[idx - lineStartPos + 1];
                Array.Copy(buff, lineStartPos, lineBytes, 0, lineBytes.Length);
                string line = encUTF8.GetString(lineBytes);
                if (inDiff) {
                    if (line.StartsWith("dif --git")) inDiff = false;
                    else if (line.Contains("�") && (line.StartsWith("-") || line.StartsWith("+"))) 
                    {
                        var b5Line = encBig5.GetString(lineBytes, 0, lineBytes.Length);
                        if (b5Line != line)
                            line = b5Line[0] + "<BIG5>" + b5Line.Substring(1);
                    }
                }
                else if (line.StartsWith("@@")) 
                    inDiff = true;
                sb.Append(line);
                lineStartPos = idx + 1;
            }
        }
        return sb.ToString();
    }

}
"@

Add-Type -TypeDefinition $sourceCode -Language CSharp
[DiffBig5Convertor]::Convert((Resolve-Path $diffPath))

Change Chinese encoding of file always causes corruption in git diff report, this article demostrating a way to solve to issue wit a PowerShell script.


Comments

Be the first to post a comment

Post a comment