如何確保寫入檔案時「檔案名稱」與「目錄名稱」正確無誤

分享到噗浪!

當要在檔案系統 (Filesystem) 寫入檔案時必須指定完整的路徑與檔名,但有時「檔案名稱」需透過程式指定時就有可能發生「檔案名稱無效」的錯誤,這時就需要透過程式過濾掉一些無效字元,才能讓成功建立檔案。

網路上資料雜亂,使用錯誤的方法也很多,所以我通常在網路上搜尋資料時通常不敢亂用,一般都會多方資料蒐集並且自行測試驗證過後才敢放心使用。

我覺得用 .NET Framework 寫程式最忌諱 重新發明輪子 (Reinventing the wheel),正確的觀念應該是能用 .NET Framework 做出來的功能,千萬不要自行實做,這樣才能確保你的 .NET 程式在未來更有可攜性(portability) [ 例如日後程式需要移到 Linux 平台實做 ]。

像是很常用的 System.IO.Path 類別就定義了兩個非常實用的方法(method),分別是以下兩個:

透過這兩個方法就可以得知在目前平台下 [如: Windows] 對於「目錄名」與「檔名」的被限制一定不能使用的字元有哪些,除此之外的字元都可以當作檔名或目錄名,例如中文或其他 Unicode 字元。

除了被限制的字元外,在檔名最後面不能出現小數點,也就是你可以沒有副檔名,但你不能用「空白」的副檔名,因此這類情況也必須將結尾的小數點全部刪除才行。

最後,我總結出以下程式:

static string MakeFilenameValid(string filename)
{
    if (filename == null)
        throw new ArgumentNullException();

    if (filename.EndsWith("."))
        filename = Regex.Replace(filename, @"\.+$", "");

    if (filename.Length == 0)
        throw new ArgumentException();

    if (filename.Length > 245)
        throw new PathTooLongException();

    foreach (char c in System.IO.Path.GetInvalidFileNameChars())
    {   
        filename = filename.Replace(c, '_');
    }
        
    return filename;
}

static string MakeFoldernameValid(string foldername)
{
    if (foldername == null)
        throw new ArgumentNullException();

    if (foldername.EndsWith("."))
        foldername = Regex.Replace(foldername, @"\.+$", "");

    if (foldername.Length == 0)
        throw new ArgumentException();
    
    if (foldername.Length > 245)
        throw new PathTooLongException();
    
    foreach (char c in System.IO.Path.GetInvalidPathChars())
    {
        foldername = foldername.Replace(c, '_');
    }

    return foldername;
}

除了檔案系統外,在各種不同的應用系統或平台對於目錄與檔名的限制也不一定一樣,例如 SharePoint 就對目錄名與檔名的限制更多,相關資訊可參考以下相關連結。

相關連結

  

此文章由 will 發表於 2009/10/19 上午 11:59:00

永久連結 | 評論 (9) | 此文章的RSSRSS comment feed |

分類: .Net | C#

標籤: , , , , , , ,

評論

十月 20. 2009 09:33

ChrisTorng

MakeFilenameValid 這種函式感覺怪怪的,不知道要用在什麼用途? 如果是使用者輸入錯誤的檔名,應該是檢查出錯誤並要求使用者更正,而不是自作聰明改成合法檔名儲存。如果是組態中設定的檔名,我認為應該要丟出例外拒絕執行。以這樣的觀點的話,我會覺得需要實作的函式是 bool CheckFilenameValid(string filename)...

另外,filename == "" 若改成 filename.Length == 0 效率會更好...

還有,if (foldername.EndsWith(".")) 為何放在第二個? 我會建議放在 if (foldername.Length > 245) 之後。畢竟,若是 Length == 0 || > 256 都要丟出例外了,此時這一行就變成是多餘的了...

ChrisTorng 台灣

十月 20. 2009 11:00

Will 保哥

ChrisTorng: 回應如下:

1. 我有個需求是從網站下載檔案,為了避免重複檔名的問題,我想把整個網址都轉成檔名,為避免出現無效字元且檔案一定要儲存,所以會採用這種寫法。

2. 沒錯,我已改成 filename.Length == 0

3. 因為我要先將非法的 "." 從字串結尾刪掉,然後再判斷長度。因為我的目的是「檔名」的來源不可靠,而檔案又一定要被儲存,所以才用這種寫法。要檢查檔案是否有效應該用 Path.GetFullPath() [ msdn.microsoft.com/.../...io.path.getfullpath.aspx ] 就可以解決。

Will 保哥 台灣

十月 20. 2009 13:49

tomexou

Path.GetFullPath()只會取得正確的系統路徑,但"不確保"檔案會存在。

msdn:
path 指定的路徑不一定要存在。例如,如果 c:\temp\newdir 是目前目錄,對 test.txt 這類檔案名稱呼叫 GetFullPath,便會傳回 c:\temp\newdir\test.txt,而該檔案並不一定要存在。

tomexou 台灣

十月 20. 2009 14:55

Will 保哥

tomexou:  是我打錯字了,我應該這樣打才對:

要檢查「檔案名稱」是否有效應該用 Path.GetFullPath() [ msdn.microsoft.com/.../...io.path.getfullpath.aspx ] 就可以解決。

Will 保哥 台灣

十月 21. 2009 12:50

Leo

感謝分享, 受益頗多.

Leo 美國

十月 21. 2009 14:12

ChrisTorng

小數點應該也不能出現在最前面...沒試過...
當然如果是完整網址,包含 http:// 的話,是不可能有小數點在前面。但既然要在網站上公開,還是應該寫得更完整,以便使用者引用時不會出現意料之外的結果。至少,文章中並沒有提及這個函式是為了將網址轉為檔名之用途而設計。

不過這裡又分檔名與資料夾名...如果是網址那應該只是一整個字串...還是要依最後一個 "/" 來切出路徑與檔名嗎?

另外,講到檔案一定要儲存,那超過 256 字元的網址應該很容易遇得到...傳回例外就違反了一定要儲存的需求...

當然以上是吹毛求疵啦...不用理我...

ChrisTorng 台灣

十月 21. 2009 14:13

ChrisTorng

MSDN 中「這個方法傳回的陣列不保證包含在檔案和目錄名稱中為無效字元的完整集合。」這句顯得很奇怪...

ChrisTorng 台灣

十月 21. 2009 16:35

Will 保哥

ChrisTorng:

我原本也以為「小數點應該也不能出現在最前面」,但事實上對檔案系統而言是可以建立該檔案的,只是有些應用程式無法讀取這類奇異檔名的檔案而已。

Will 保哥 台灣

六月 2. 2010 22:24

hana

您好,關於路徑名稱的問題
我使用
Process p = new Process();
p.StartInfo.FileName = @"C:\Program Files\Windows Media Player\wmplayer.exe";
p.StartInfo.Arguments = @"C:\Documents and Settings\Administrator\My Documents\My Music\001.mp3";
會發生找不到檔案,傳入的路徑只到C:\Documents這個部份,是因為路徑名稱不能有空白字元嗎?

hana 台灣

新增評論


( 您輸入的Email不會顯示於網站上 )

  Country flag

biuquote
  • 評論
  • 線上預覽
Loading