1

再探 Windows 檔名不分大小寫陷阱 - .NET 處理對策

 1 year ago
source link: https://blog.darkthread.net/blog/dotnet-filename-case-issue/
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

再探 Windows 檔名不分大小寫陷阱

先幫沒踩過的同學補充這顆小地雷 - Windows 檔名不分大小寫阱陷

我們都知道 Windows 的檔案系統不區分大小寫,對 Windows 來說,Logo_TW.png 跟 Logo_tw.png 是同一個檔案。因此,你無法在檔案總管將 Logo_TW.png 更名成 Logo_tw.png、logo_tw.png、LOGO_TW.PNG。(如下圖)

Fig1_637599657193586173.gif

一般來說,檔名沒精準調成指定大小寫組合問題不大,反正字元一致便能找對檔案,大小寫不同無所謂。但如果你將檔名拉到 .NET 進行比對,大小寫不一致就會是個要處理的問題。(另一個類似情境是 SQL WHERE 比對時會忽略字尾空白,將欄位讀到 .NET 再比較,結尾空白會導致在 SQL 相等的資料在 .NET 世界不相等,有處理過 CHAR()/NCHAR() 型別的老鳥應該知道讀完先 Trim() 的 SOP)

今天再遇到類以的檔案同步情境,又踩到檔名只差在大小寫不同無法覆寫一致檔名的問題。這回我想改變戰略 - 既然檔名由系統全權控制,何不一開始就讓檔名完全一致,而不是每次要比對時都忽略大小寫,感覺更合理有效率。

研究了一下,解法出奇簡單,File.WriteAllText()/WriteAllBytes() 遇到只有大小寫不同的同樣檔名會維持舊檔名,但用 FileInfo.Move() 或 File.Move() 即可強制換成指定的大小寫組合,使用以下範例驗證:

Action chkFileName = () => {
    Console.ForegroundColor = ConsoleColor.Cyan;
    Console.WriteLine(Directory.GetFiles(".", "*.txt").First());    
};
Action<string> printTitle = (msg) => {
    Console.ForegroundColor = ConsoleColor.Yellow;
    Console.WriteLine(msg);
};

Directory.SetCurrentDirectory("D:\\");
var fileName = "test.txt";
if (File.Exists(fileName))
{
    File.Delete(fileName);
}

printTitle($"Write {fileName}");
File.WriteAllText(fileName, "Hello World");
chkFileName();

var upCaseFileName = fileName.ToUpper();
printTitle($"Write {upCaseFileName}");
File.WriteAllText(upCaseFileName, "Hello World");
chkFileName();

printTitle($"FileInfo.MoveTo() {upCaseFileName}");
FileInfo fi = new FileInfo(fileName);
fi.MoveTo(upCaseFileName);
chkFileName();

printTitle($"File.Move() {upCaseFileName} to {fileName}");
File.Move(upCaseFileName, fileName);
chkFileName();

Console.ResetColor();

如下圖,File.WriteAll("TEST.TXT", ...) 不會將 "test.txt" 檔名換成 "TEST.TXT",但 FileInfo.MoveTo()、File.Move() 可以:

Fig1_638283110904967107.png

掌握這點,我們只需在每次寫入檔案後,檢查檔名是否不一致,若不相同就用 FileInof.MoveTo() 更名,搞定收工。

var upCaseFileName = fileName.ToUpper();
printTitle($"Write {upCaseFileName}");
File.WriteAllText(upCaseFileName, "Hello World");
// 檢查檔案名稱,若大小寫不同,使用 MoveTo 更名
var fi = Directory.GetFiles(
    Path.GetDirectoryName(Path.GetFullPath(upCaseFileName)), 
    Path.GetFileName(upCaseFileName))
    .Select(x => new FileInfo(x)).FirstOrDefault();
if (fi != null && fi.Name != Path.GetFileName(upCaseFileName)) 
{
    fi.MoveTo(upCaseFileName);
}
chkFileName();

Fig2_638283110906941425.png

【同場加映】利用 \\?\ 前置詞(延伸閱讀:冷知識 - 刪不掉的 Windows 檔案)強制更名大小寫的小技巧:

Fig3_638283135917766530.png


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK