13

更改檔案中文編碼導致 git diff 亂碼之完美解法

 2 years ago
source link: https://blog.darkthread.net/blog/git-diff-big5-beautifier/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
更改檔案中文編碼導致 git diff 亂碼之完美解法-黑暗執行緒

大家有遇到 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))

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK