

更改檔案中文編碼導致 git diff 亂碼之完美解法
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.

大家有遇到 git diff 比對文字檔,因中文編碼更改(例如 Big5 改 UTF-8)導致結果裡有一半中文變亂碼的情況嗎?我想到一個完美解法。(我自己覺得啦,不服來戰)
git diff 遇到文字檔中文編碼不同的問題之前處理過(參考:Git 實戰技巧 - 使用 git diff 比對 UTF-16/BIG5 文字檔),但定義 textconf 再用 .gitattributes 指定檔案套用編碼的做法稱不上完美,實務上有些無法處理的狀況,例如:
- 用
git diff --no-index a.txt b.txt
任意比對非 Git Repository 下的檔案,就很難用 .gitattributes 指定中文編碼 - 同一個檔案由 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))
Recommend
-
14
-
13
JavaScript 重度運算導致 IE 網頁重啟-黑暗執行緒 最近用 IE 跑一段極耗資源的 JavaScript 程式(前陣子發現的 diff2html.js),當資料量大,JavaScript 函式在 Chrome 都要跑好幾分鐘,...
-
29
中文編碼解析線上版 Ver 2.0-黑暗執行緒 中文編碼解析線上版上線已經兩年,只要對中文編碼方式存疑,開網頁貼文字看結果,多半能很快得到答案。 上週讀者 ChrisTorng 提了一個很棒的
-
11
Google Play 服務導致 Android 手機掉電經驗分享-黑暗執行緒 我的 Android 手機(Samsung S9)經歷了一次 Google Play 服務異常,手機狂掉電的狀況,實測在網路關掉待機狀況下,一個小時可以掉電 20%。試過很多處理方式都不見效,直到今天早上自己好了,雖然...
-
15
中文亂碼"蕞蕞蕞蕞"是怎麼來的? 2008-07-04 08:34 AM
-
25
ASP.NET Core JSON 中文編碼問題與序列化參數設定-黑暗執行緒 轉進 ASP.NET Core 世界,依循過去寫 MVC 經驗,加上參考網路技術文章(當然,還有流著奶與蜜的 Stackoverflow) 大致還算都順利,但不時發現新的眉角。 今天遇到的問題是
-
2
【茶包射手筆記】F12 偵錯導致 window.open() 快顯封鎖-黑暗執行緒 現代瀏覽器對 window.open() 行為有諸多限制,以防止其被濫用。瀏覽器多內建有快顯封鎖器 (Popu...
-
8
當心 Windows KB5018410、KB5018418 更新導致 TLS/SSL 相關應用程式異常-黑暗執行緒 學新東西時最怕有其他系統問題來亂,你通常先檢討自己程式沒寫好或漏掉什麼關鍵,查又查試了又試,依然連簡單到靠北的範例都做不出來,沮喪到懷疑人生...
-
9
HTTP/2 Zero-Day 漏洞導致破紀錄的 DDoS 攻擊 10/10/2023 12 min read
-
6
C# 連線 HTTPS 網站發生驗證失敗導致基礎連接已關閉-黑暗執行緒 某台透過 .NET WebClient 物件爬網頁抓資料排程忽然出現: 基礎連接已關閉: 傳送時發生未預期的錯誤。 ---> System.IO.IOException: 驗證失敗,因為遠端群...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK