每位老司機,對常見程式演算需求多半有自己一套方法,兵來將擋,水來土掩,行之有年。甚至有些寫法在 VBScript 時代習得,之後搬到 JavaScript 用過,到了 .NET 稍調語法繼續發光發熱。舉個例子,在處理檔案路徑時,有時要判斷相對路徑或絕對路徑做不同處理,我慣用的解法是寫個 IsAbsolutePath 之類的函式,判斷路徑最前方是否為 X:\ 磁碟機代號、\\server-name\... UNC 格式來決定。類似這樣:

這其實也沒什麼不對,同樣邏輯上線跑了近二十年沒出過差錯。不過,當知道在 .NET 可以不用自己寫函式判斷,系統有內建 (System.IO.Path.IsPathRooted()) 而且從 .NET 1.1 就有,就覺得自己耍笨了。

這篇來整理大家可能錯過的 System.IO.Path 好用函式

  1. 組裝路徑
    不要再 "C:\Temp" + fileName 串字串了,Path.Combine(folderName, fileName) 能識別多餘 \ 結尾,處理絕對路徑,還能一次串接多節目錄:
    Path.Combine("C:\\Temp", "test.txt"); // C:\Temp\test.txt
    Path.Combine("C:\\Temp\\", "test.txt"); // C:\Temp\test.txt
    Path.Combine("C:\\Temp\\", "2022", "test.txt"); // C:\Temp\2022\test.txt
    Path.Combine("C:\\Temp\\", @"\Workspace", "test.txt"); // \Workspace\test.txt
    
  2. 取得目錄、檔名、副檔名
    不要傻傻用 Split('\\')、Split('.') 自己拆,不管目錄、檔名、副檔名都有現成函式
    Path.GetDirectoryName(@"C:\Temp\2022\test.txt"); // C:\Temp\2022
    Path.GetDirectoryName(@"\Temp\test.txt"); // \Temp
    Path.GetFileName(@"\Temp\test.txt"); // test.txt
    Path.GetFileNameWithoutExtension(@"C:\Temp\2022\test.txt"); // test
    Path.GetExtension(@"C:\Temp\2022\test.txt"); // .txt
    
  3. 取得完整路徑
    可會依目前所在目錄計算絕對路徑
    Path.GetFullPath(@".\test.txt"); // 由目前工作目錄計算絕對路徑
    Path.GetFullPath(@"C:\Temp\test.txt") // C:\Temp\test.txt
    
  4. 檢查路徑是否為從根目錄起始(絕對路徑)
    Path.IsPathRooted(@"C:\Temp"); // true
    Path.IsPathRooted(@"\Temp"); // true
    Path.IsPathRooted(@".\test.txt"); // false
    
  5. 取得根目錄
    Path.GetPathRoot(@"Temp\test.txt"); // 空字串
    Path.GetPathRoot(@"\Temp\test.txt"); // \
    Path.GetPathRoot(@"C:\Temp\test.txt"); // C:\
    Path.GetPathRoot(@"\\my-server\share\folder1\test.txt"); // \\my-server\share
    
  6. 更換副檔名
    Path.ChangeExtension(@"C:\Temp\test.txt", ".jpg"); // C:\Temp\test.jpg
    
  7. 取得暫存目錄、暫存檔名(隨機產生且不重複)
    Path.GetTempPath(); // 例:C:\Users\Jeffrey\AppData\Local\Temp\
    Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); // 可拋式暫存檔
    Path.GetTempFileName(); // 不重複隨機檔名,如:C:\Users\Jeffrey\AppData\Local\Temp\tmpB913.tmp
    
  8. 檢查路徑或檔名是否包含無效字元
    @"C:\Temp\".IndexOfAny(Path.GetInvalidPathChars()); // -1
    @"C:\?Temp""\".IndexOfAny(Path.GetInvalidPathChars()); // 8, ? 合法 " 不合法
    @"test?.txt".IndexOfAny(Path.GetInvalidFileNameChars()); // 4
    
    順便列出無效路徑與無效檔名字元,無效檔名再多了 :、*、?、\,/ 五個字元

使用 Path 函式的另一大好處是確保跨平台,升級至 .NET Core / .NET 5+,程式有可能是在 Linux/macOS 執行,光路徑分隔符號就跟 Windows 就不一樣,寫死 outputFolder + "\" + subFolderName + "\test.txt 在非 Winodws 平台肯定爆炸,但 Path.Combine(outputFolder, subFolder, "test.txt") 會依所在平台決定路徑分隔符號,正確又省力。所以,現在就開始養成用 Path 函式處理檔名與路徑的習慣吧!

Introduction to the System.IO.Path functions.


Comments

# by 無名

感謝提示

# by Ike

為什麼文章分類會是 HTML 呢?

# by Jeffrey

to lke, 謝謝提醒,這是已知問題,新文章若沒指定分類會繼承前一篇文章的分類,不知道是客戶端是 API 端問題。因只有忘設分類時會發生,所以擱置很久沒處理,且估計會繼續發懶下去,噗。

# by Ike

送出前 檢核並提示 未選分類 呢?

# by ByTim

Path 函式有辦法轉換檔案的 \xxx\aaa.jpg 成網站的 /xxx/aaa.jpg 嗎? 指定轉換可以? 還是簡單的Replace就好?

# by Jeffrey

to ByTim,Path.DirectorySeparatorChar 會依平台傳回 \ 或 /,如果你是寫死 \ 的路徑字串要轉成寫死 / ,我會用 Replace 處理。

# by ByTim

to Jeffrey,好的,看來直接Replace比較快,謝謝回復。

# by Ray

只是查 Path.Combine 的用法連進來的,看到幾個問題 在 1. 組裝路徑 的程式碼有些部分有點問題,例 第三行的 2022 在註解變成 20222 第四行的 \W 應該是不能寫的 另外我蠻好奇為什麼在第二個參數前加\會變成從\開始讀 但如果第二個參數前沒有\而是第一個參數後多加幾個\就還是從C:開始讀 實際測試 Path.GetFullPath(@"\Workspace\test.txt") 查路徑的話會回傳 C:\Workspace\test.txt 這跟 MSDN 上寫的又不一樣 從 MSDN 的範例來看,沒指定根目錄的 Path.GetFullPath 應該是在 專案\bin\debug\ 下才對

# by Jeffrey

to Ray, Path.Combine 部分 20222 及 @"\W..." 缺 @ 已更正,感謝指正。 Path.GetFullPath(@"\Workspace\test.txt") 傳回什麼目錄跟 C# 當時的工作目錄有關,可用 Directory.GetCurrentDirectory() 比對,若工作目錄在 C 槽會得到 C:\Workspace,若在 D 槽則會傳回 D:\Workspace

# by CKD

謝謝, 受用了~

Post a comment