再探 Windows 檔名不分大小寫陷阱 - .NET 處理對策
5 |
先幫沒踩過的同學補充這顆小地雷 - Windows 檔名不分大小寫阱陷。
我們都知道 Windows 的檔案系統不區分大小寫,對 Windows 來說,Logo_TW.png 跟 Logo_tw.png 是同一個檔案。因此,你無法在檔案總管將 Logo_TW.png 更名成 Logo_tw.png、logo_tw.png、LOGO_TW.PNG。(如下圖)
一般來說,檔名沒精準調成指定大小寫組合問題不大,反正字元一致便能找對檔案,大小寫不同無所謂。但如果你將檔名拉到 .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() 可以:
掌握這點,我們只需在每次寫入檔案後,檢查檔名是否不一致,若不相同就用 FileInfo.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();
【同場加映】利用 \\?\
前置詞(延伸閱讀:冷知識 - 刪不掉的 Windows 檔案)強制更名大小寫的小技巧:
The file system of Windows always ignore file name letter case sometimes cause issue, this article provide a easy way to fix it.
Comments
# by ChrisTorng
奇怪,我在 Windows 10/11 Beta 檔案總管中變更檔名,都可以換大小寫啊,剛剛再試也是一樣。我從不記得有無法換大小寫的問題!?
# by Jeffrey
to ChrisTorng,試了一下的確如此,檔案總管可以改大小寫。 查了一下,在不同作業系統版本、NTFS 或 FAT32/exFAT、檢視模式的結果不一樣。https://reurl.cc/RzxLG9 (List, Icons, Tiles 時,改成功但要 F5 重新整理才會看到正確結果)
# by felix
"若不相同就用 FileInof.MoveTo() 更名", typo error
# by Jeffrey
to felix, 已更正,謝。
# by felix
To Jeffrey, 你的文章對我寫 C# 程式幫助很大,謝謝分享!