Windows的MAX_PATH

來源:互聯網
上載者:User

標籤:des   blog   http   java   使用   檔案   

MAX_PATH的解釋: 檔案名稱最長256(ANSI),加上盤符(X:\)3位元組,259位元組,再加上結束符1位元組,共260
http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
http://support.microsoft.com/kb/320081


windows下,全檔案名稱的字元長度是有限制的,眾所周知,是MAX_PATH,260。對於這個,幾點具體解釋:
1. 這個260指的是包含目錄到檔案名稱的全路徑字元長度。
2. 實際上,測試可以發現:
--加上null,你在資源管理員裡只能建立259個字元的全檔案名稱,即實際上只能建立MAX_PATH -2 = 258字元的路徑。
--你可以建立c:\長目錄\abc.txt <= 258,也可以建立c:\abc\長檔名.txt <= 258。
--建立每一級目錄的時候,可以輸入的目錄名字元是有限制的,規則就是:至少保留下了 11個字元(8.3規則?)使得最內層目錄仍然可以建立出檔案。
--但是,比如你建立了C:\abc\長檔名.txt,然後呢,重新命名目錄abc,這時候,你可以建立出一個總長度超過MAX_PATH的全檔案名稱。相當tricky。

參考: http://blog.163.com/[email protected]/blog/static/3606944620105109561679/


------------------------------ 解決方案一 ------------------------------
http://www.debugease.com/csharp/1202792.html
那你不要用它的全路徑來建立,用某個檔案夾的DirectoryInfo來建立不就好了
多謝提醒,取得父資料夾路徑CreateSubdirectory就可以了。試了好多方法,怎麼把這個忘了,汗一下自己。

讓我們從BCL中的一個有趣的異常開始今天的話題:[PathTooLongException]: The specified path, file name, or both are too long. The fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters.我們的客戶在bug報告裡說: “路徑最多隻有260個字元? MS搞笑的吧. 把這個限制搞得更長一些!”. 在這裡我將會對這些提交bug報告的人(很抱歉你們的bug被關成了”won’t fix”)詳細解釋這個問題並告訴你們我們對此所作出的努力.讓我們先來澄清一些術語:Path: 一個檔案的全路徑. 比如你又一個檔案: c:\temp\fileA.txt, 那麼通常你會叫這個檔案fileA.txt, 但它的全路徑應該是c:\temp\fileA.txt.
MAX_PATH: Windows API定義的路徑的最大長度, 260個字元.
Long path: 一個長度超過了MAX_PATH的路徑.
Long file name: 跟long path還不一樣. 這個其實是用來跟短檔案名稱作對比的, 就是以前我們說的那個8.3格式的檔案名稱.
眾所周知.NET API是依賴於Windows API的, 從這一點上看, 上面的這個異常就沒有什麼問題了. 然而Windows API還提供了一個方法來繞過這個MAX_PATH的限制. 如果你在你的檔案路徑前面加上”\\?\”的首碼, 然後調用unicode版本的Windows API, 那麼你的path的最大長度就可以達到32k了. 也就是說你只要加上首碼”\\?\”就可以在Windows API中使用long path了.沒有人會抱怨32k的長度限制了, 那麼是不是就可以說問題解決了呢? 也不完全是. 過去我們不願意支援long path是有原因的, 而且現在我們還會考慮這些原因. 第一個原因就是安全. 首碼”\\?\”並不僅僅是打破的long path的限制, 它還能讓path在到達檔案系統之前只受到Windows API的最小的修正. 這樣做的結果就是”\\?\”規避了Windows API對於path的一系列的標準化的操作: 去掉path後面的空格, 把’.’和’..’擴充為相應的內容, 以及把相對路徑轉換成全路徑等等. 在.Net中的如果用FileIOPermission attribute來保證安全, 我們就不得不使用標準化後的路徑. 而不用FileIOPermission就會有安全隱患. 現在我們明白了如果我們用首碼”\\?\”來解決long path的問題的話, 我們就必須能像Windows API那樣把路徑標準化.第二個原因是支援long path可能導致的不一致行為. 很多操作檔案的Windows API都支援以”\\?\” 作為首碼的long path, 但僅僅是很多而不是全部. 比如LoadLibrary, 它的功能是將一個module映射到調用者的地址空間, 在檔案路徑超過MAX_PATH的時候就會失敗. 這就意味著你可以調用MoveFile把一個DLL放到一個路徑長度超過MAX_PATH的地方, 但是當你想載入這個DLL的時候卻失敗了. 在Windows API裡面有很多這樣的例子, 雖然有一些權宜之計, 但都是針對特殊問題的, 沒有一個通用的解決方案.另外一個因素, 也是最痛苦的一個, 是Windows Application和Windows shell本身在long path上的相容性. 因為Windows shell本身只支援長度小於260的路徑 (下面會講到Vista shell弱化了這個限制). 就是說如果.NET支援了long path, 那麼你就可以通過你的.NET App建立一些在Explorer或是命令列中不能訪問的檔案了. J我們已經意識到了260個字元的限制並不是很合理. 我們的客戶並不經常碰到這個問題, 但是一旦需要一個超出MAX_PATH的路徑, 就會覺得很不方便. 一個權宜之計是P/Invoking Windows API並使用”\\?\”首碼, 但是這樣就不得不寫一大坨跟System.IO重複的code. 所以為瞭解決這個問題, 我們的客戶常常會重新設計目錄結構, 絞盡腦汁的縮短目錄名. 因為這個問題已經逐漸層得普遍, 所以無論是.NET framework還是別的領域, MS都已經開始著手解決這個問題. 實際上在vista中你應該已經可以看到我們為了減少出現MAX_PATH的問題的幾率所作出的改動: 很多特定的目錄名已經被縮短 (譯註: \Documents and Settings à \Users, 實際上, 在MS有一個專門的alias叫longpath來談論這個問題), shell還有一個auto-path shrinking的功能, 它會用比較短的別名來表示路徑以把那些long path壓縮在260個字元以內.


------------------------------ 解決方案二 ------------------------------
http://express.ruanko.com/ruanko-express_11/webpage/tech-overnight_1.html

上層邏輯控制和交易處理使用 Java 開發,而底層核心功能使用 C/C++ 實現,這已經成為一種較為通用的開發模式。但由於 Windows 作業系統的預設設定,上述語言在對長路徑名(>260 字元)檔案的處理時會遇到一些問題。本文列出了不同的 JDK 版本在 Windows 作業系統上對於長路徑名檔案處理的區別,給出了兩種支援長路徑名檔案的 C/C++ 編程方法,同時還指出了從 JDK 5.0 開始才完全支援長路徑名。使用本文的方法,可以解決在 Windows 平台上標準 API 函數對長路徑名檔案支援的局限性問題,給開發測試工作帶來方便。

Windows 對長路徑名檔案的限制

眾所周知,微軟的檔案系統經曆了 fat->fat32->NTFS 的技術變革。且不論安全和檔案組織方式上的革新,單就檔案名稱而言,已經從古老的 DOS 8.3 檔案格式(僅支援最長 8 個字元的檔案名稱和 3 個字元的尾碼名)轉變為可以支援長達 255 個字元的檔案名稱。而對於路徑長度,NTFS 也已經支援長達 32768 個字元的路徑名。

然而,Windows 作業系統並沒有完全放開路徑名長度的限制,在 windef.h 中,可以找到如下的宏:

#define MAX_PATH 260

事實上,所有的 Windows API 都遵循這個限制。因此,每當我們試圖更改某一檔案的檔案名稱時,當輸入的檔案名稱長度 ( 全路徑 ) 到達一定限度時,雖然檔案名稱本身還未達到 255 個字元的限制,但是任何輸入將不再被接受,這其實正是由於作業系統不允許 260 個字元(byte)的檔案全路徑。

實際應用中,這種 260 個字元的全路徑的限制給應用開發帶來了很大的不便。試想如下應用:我們希望給應用伺服器增加一個本地 cache 的功能,該功能可以把遠程伺服器上的檔案留下一個本地的副本。一個合理的實現可以把 url 映射為檔案名稱,當 url 很長時,cache 檔案的長度也會很長。當檔案名稱長度超過 255,我們可以把對應檔名的前 255 個字元作為目錄名稱。但是,我們仍然無法解決 260 個字元的全路徑限制。另外,如果一個應用軟體的目錄結構過深,很容易出現某些檔案名稱長度(含路徑)超過 260 個字元,並因此造成安裝或刪除的失敗。總而言之,該限制給我們的開發測試工作帶來了諸多不便。

對於一些網路伺服器,往往需要將 Java 代碼用於上層邏輯控制 / 交易處理的開發,同時將 C/C++ 用於底層核心功能的實現。為此,我們研究了這兩種程式語言對長路徑名檔案的支援情況。其中,對於 Java,比較了兩個常用版本 1.4 和 5.0 對長路徑支援的差異性;對於 C/C++ 語言的局限性,提出了我們的解決方案。

實驗環境

 

作業系統: Windows xp
檔案系統: NTFS 檔案系統
Java 編譯環境:JDK 1.4.2 以及 JDK 5.0
C++ 編譯環境: VC.net
在 Java 中使用長路徑名檔案

Java 語言並不需要對長路徑名檔案進行特殊的處理,就可以支援長路徑名檔案的建立、讀寫和刪除操作等基本操作。但是,JDK 1.4.2 和 JDK 5.0 在長路徑的支援上是不同的,JDK 1.4.2 並不是完全支援所有的長路徑名檔案操作,比如訪問檔案屬性的操作是不支援的。我們設計了如下代碼來驗證 JDK 1.4.2 和 JDK 5.0 對長路徑名檔案支援的區別。

清單 1. 對長路徑名檔案操作的 Java 實驗代碼:

try {    String fileName = "E:\\VerylongpathVerylongpathVerylongpath        VerylongpathVerylongpathVerylongpathVerylongpath        VerylongpathVerylongpathVerylongpathVerylongpath\VerylongpathVerylongpathVerylongpathVery        longpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpa        th.txt";    System.out.println("Filename: " + fileName);    System.out.println("File path length: " + fileName.length());    String renameFileName = "E:\\VerylongpathVerylongpathVerylongpath        VerylongpathVerylongpathVerylongpathVerylongpath        VerylongpathVerylongpathVerylongpathVerylongpath\\Short.txt";    //Create the file.    File file = new File(fileName);    if (!file.exists())        file.createNewFile();    if (file.exists())        System.out.println("The file exists!");    if (file.canRead())        System.out.println("The file can be read!");    if (file.canWrite())        System.out.println("The file can be written!");    if (file.isFile())        System.out.println("It‘s a file!");    //Write to the created file.    FileOutputStream out = new FileOutputStream(file);    PrintStream p = new PrintStream(out);    p.println("This is only a test!");    p.close();    //Read the information from that file.    BufferedReader br = new BufferedReader(new FileReader(file));    StringBuffer sb = new StringBuffer();    while (true) {        String sl = br.readLine();        if (sl == null) {            break;        } else {            sb.append(sl + "\n");        }    }    br.close();    System.out.println("The content in the file:");    System.out.print("\t" + sb.toString());    //File rename    File newfile = new File(renameFileName);    if (newfile.exists())        System.out.println(renameFileName + "exsited");    else {        if (file.renameTo(newfile)){            System.out.println("Rename sucessful!");        } else {            System.out.println("Rename failed!");        }    }    //delete file    if (file.delete())        System.out.println("The old file deleted!");    if (newfile.delete())        System.out.println("The renamed file deleted!");    }  catch (IOException e) {        //Error happened        e.printStackTrace();        System.out.println("Error occurs in writing to the file.");    }}

清單 2. 使用jdk1.42 的結果

Filename: E:\VerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpath.txt
File path length: 272
The content in the file:
This is only a test!
Rename failed!The old file deleted!

從實驗結果來看,JDK 1.4.2 得到了該長路徑名檔案的內容,因此,對於該長路徑名檔案的建立以及讀寫操作都是支援的。但是對比下文使用 JDK 5.0 的結果,可以看到,所有對於檔案屬性的判斷都是錯誤的,同時,重新命名的操作也無法實現。更為重要的是,JDK 1.4.2 存在著一個很致命的問題,即方法 File.exists() 是失效的。通常,在刪除檔案前,需要調用該方法判斷檔案是否存在,對於 JDK 1.4.2,如果直接去刪除一個不知道是否存在的檔案,就會存在比較大的風險。因此,JDK 1.4.2 在 Windows 平台對長路徑名檔案的操作只是有限的支援,使用的時候,一定要注意。

清單 3. 使用jdk5.0 的結果

Filename: E:\VerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpath.txtFile path length: 272The file exists!The file can be read!The file can be written!It‘s a file!The content in the file:This is only a test!Rename sucessful!The renamed file deleted!

從實驗中可以清楚的看到,在版本 JDK 5.0 中,所有的檔案操作(建立、讀寫、屬性操作、重新命名、刪除等)都能夠得到正確的處理。使用 JDK 5.0 就可以完全不用擔心長路徑名檔案的使用問題。

在 C/C++ 中使用長路徑名檔案

相對於 JDK 5.0 不需要任何改動就可以支援長路徑名檔案,在 C/C++ 中使用超過 260 個字元的路徑長度的檔案,會複雜得多。下面介紹兩種支援長路徑名檔案的方法。

方法一:使用 Unicode 版本的 API

從微軟官方網站 Path Field Limits,可以查到,使用 Unicode 版本的 API,對於使用 NTFS 檔案系統的 Windows NT 4.0, Windows 2000, Windows XP Home Edition, Windows XP Professional 和 Windows Server 2003 作業系統,可以支援 32768 位元組的檔案路徑長度。同時,路徑名必須使用 \\?\ 的首碼。依照這個思路,我們設計了實驗。

清單 4. 對長路徑名檔案操作的 C 的範例程式碼(Unicode API)

{FILE *from, *to;char filename[1024];strcpy(filename,"\\\\?\\E:\\VerylongpathVerylongpath
VerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpath
\\VerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpath
VerylongpathVerylongpathVerylongpathVerylongpathVerylongpath.txt");int iL1=MultiByteToWideChar(CP_ACP, 0, filename,
strlen(filename), NULL, 0); WCHAR* wfilename=new WCHAR[iL1+1]; wfilename[iL1] = ‘\0‘;int iL2=MultiByteToWideChar(CP_ACP, 0, filename,
strlen(filename), wfilename, iL1); from = _wfopen( wfilename ,L"rb");to = fopen(".\\longpath.txt", "wb");if((from ==NULL)||(to==NULL)) return -1;char buffer[1024];int count = 0;while ( (count = fread(buffer, sizeof(char), 1024, from)) != 0) fwrite( buffer, sizeof(char), count, to);delete []wfilename;fclose (from); fclose(to);}

使用如上的方法,我們可以拷貝某長路徑名的檔案到當前檔案夾中。從實驗結果看,該方法是有效。但是,由於該方法要求系統使用 Unicode 的 API,同時需要更改路徑名稱以及編碼方式。因此,對於一個已經存在的系統,由於需要改變所有檔案操作相關的 API,因此改動將會很大。

方法二:建立 8.3 格式的短路徑名

對於每一個長路徑名,都有一個 8.3 格式(8 個字元的檔案名稱和 3 個字元的尾碼名)的短路徑名與其相對應,任意的檔案夾或者檔案名稱都可以映射成一個 8 字元的檔案名稱(A~B),其中 A 是檔案名稱首碼,B 是表示字母序的順序。作業系統可以保證這樣的映射是一對一的,只要使用 GetShortPathName() 將長路徑名轉成相應的短路徑名,就可以進行對該檔案進行普通的檔案操作。同時,在任何時候都可以用函數 GetLongPathName() 把 8.3 格式的短路徑名恢複成初始的長路徑名。

如 GetShortPathName Function 敘述,我們需要一個 Unicode 版本的 API,同時在路徑名前加上 \\?\ 的首碼,才能實現長短路徑名間的切換。但從實驗來看,即使不使用 Unicode 的 API,依然可以實現上述功能。

清單 5. 對長路徑名檔案操作的 c 的範例程式碼(ShortPath)

{char pathName [1024];strcpy(pathName,"\\\\?\\E:\\VerylongpathVerylongpathVerylongpath
VerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpath
\\VerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpathVerylongpath
VerylongpathVerylongpathVerylongpathVerylongpath.txt");const int MaxPathLength = 2048;char shortPath[MaxPathLength];if (strlen(pathName) >= MAX_PATH){ char prePath[] = "\\\\?\\"; if (strlen(pathName) >= MaxPathLength - strlen(pathName)) return false; sprintf(shortPath, "%s%s", prePath, pathName); for (int iPathIndex = 0; iPathIndex < strlen(shortPath); iPathIndex++) if (shortPath[iPathIndex] == ‘/‘) shortPath[iPathIndex] = ‘\\‘; int dwlen = GetShortPathName(shortPath, shortPath, MaxPathLength); if (dwlen <= 0) return false;}}

經過上述的代碼,超過 MAX_PATH 限制的路徑名都可以轉變成一個 8.3 格式的短路徑名,可以把這個檔案名稱 (shortPath)作為後續檔案操作函數的參數。這種情況下,對於該檔案的所有操作都可以被支援了。我們用這種縮短路徑名長度的方式解決了長路徑名檔案的操作問題。

結束語

本文首先列出了不同的 JDK 版本在 Windows 作業系統上對於長路徑名檔案處理的區別,同時指出了 JDK 5.0 開始才完全支援長路徑名;在第二部分中給出了兩種支援長路徑名檔案的 C/C++ 編程方法。使用上文中的任一方法,我們都可以實現對長路徑名檔案的操作,這將在很大程度上方便我們的開發工作,解決在 Windows 平台上標準 API 函數對長路徑名檔案支援的局限性問題。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.