8.3 路徑,檔名,路徑,名稱空間,名稱空間,最大檔案長度,檔名,路徑名
https://docs.microsoft.com/zh-cn/windows/desktop/FileIO/naming-a-file
命名檔案、路徑和名稱空間
Windows 支援的所有的檔案系統都使用檔案和目錄的方式來訪問磁碟或者裝置上的內容。開發人員應該瞭解,windows API 中的各種規則,約定,以及檔案、目錄的名稱限制。
可以通過檔案I/O API 訪問磁碟、裝置或網路共享上的資料。檔案和目錄,以及名稱空間,都是路徑的概念的一部分,路勁即一個字串,它代表了統一的該去哪裡找到資料的規則,而不是針對不同的儲存方式使用不同的表示方法(磁碟、裝置、網路連線等)。
一些檔案系統,比如NTFS,支援連結檔案和目錄,像普通的檔案和目錄一樣,其也遵守檔案命名約定和規則。
檔案和目錄名
所有的檔案系統遵循單個檔案的相同通用命名約定:一個基本的檔名稱+可選的副檔名,中間用“.”分割。但不同的檔案系統,它們有特定的規則,關於如何構建路徑的各個獨立的部分,以及如何構成路徑或檔案。目錄只是具有特殊屬性的檔案,它需要遵守與常規檔案相同的命名規則。因為術語目錄,簡單的指檔案系統中一種特殊的檔案,一些資料將使用通用術語檔案,來概括目錄和資料檔案的概念。因此,除非特別指明,任何對於檔案的命名或使用規則或例子,對於目錄來說都是適用的。術語路徑,一個或多個目錄,反斜槓,以及可選的卷名。
字元數限制的規則比較複雜,依賴於底層的檔案系統和所使用的目錄名字首格式。因為向後相容機制,這一部分特別複雜。比如,更老的MS-DOS FAT檔案系統來說,基本檔名最長8字元,副檔名最多3 字元,包括”.”在內,做多12 字元。這被稱作8.3 檔名。FAT 和 NTFS 檔案系統沒有8.3 的限制,因為它們已經有了長檔名的支援,但它們依然支援8.3 版本的長檔名。
名稱轉換
下面的基本規則使得應用程式有能力在任何檔案系統上,建立和處理合法的檔案、目錄名。
- 使用“.” 來分割基本檔名和副檔名
- 使用反斜線分離path 中的元件。Blackslash 將檔名從路徑中分割出來,並將目錄中單個的目錄名分割出來。不能在實際的目錄或檔名中使用反斜線,因為它是保留字元,用於將名稱分割成單個的元件。
- 使用反斜線作為碟符的一部分,比如:”C:\path\file” 中的”C:\”,或 “\\server\share\path\file”中的\\server\\share,這是通用命名慣例的一部分。
- 不要做,大小寫敏感的假設。NTFS 支援POSIX 的大小寫敏感的語義,但是,預設是不支援的
- 卷別名(驅動器號)不區分大小寫。
- 內碼表上的幾乎所有字元,包括擴充套件字符集中的字元(128-255)都是可用的,但不可用下面的字元:
- 數值0.
- 整數表示在1~31的字元,除非允許這些字元的備用資料流。
- 目標檔案系統所不支援的字元。
- 使用“.”表示當前目錄,比如”.\temp”
- 使用”..”表示上層目錄
- 別使用下列的保留檔名
- CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, and LPT9.
- 也不要使用這些名字,再加一個副檔名。
- 不要在檔案或目錄名的末尾加”.”,但是支援在檔案的剛開始新增”.”,比如”.temp”
檔案短名和長名
長檔名,即,擴充套件8.3 檔名得到的檔名。當你建立一個長檔名的時候,windows 可能會建立一個8.3 的別名,並存儲在磁碟上。這個8.3 別名可以在系統範圍或者某個磁碟上禁止,取決於具體的檔案系統。
Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: 8.3別名在特定的捲上是不可以被取消的。直到windows 7 和 windows server2008 R2,才可以取消。
許多檔案系統上,當檔名太長,需要以8.3 格式顯示,將包含一個“~”字元。
並不是所有的檔案系統支援,“~”轉換,且系統可以配置為關閉8.3 別名。不要預設系統上已經存在了8.3 別名。
從系統獲取檔案的8.3 檔名,長檔名,或者全路徑名時,考慮下面的三個方法:
- 8.3 ,GetShortPathName
- 從短得到長檔名,GetLongPathName
- 獲取長檔名,即從相對路徑得到全路徑等操作,詳情看msdn,GetFullPathName
在較新的檔案系統上,比如NTFS,exFAT,UDFS和FAT32,windows 使用Unicode 儲存長檔名到磁碟上,這意味著,原始的長檔名肯定是被保留的。
擁有長檔名的檔案可以在NTFS 和 FAT 檔案系統的分割槽上互相拷貝,而不會丟失檔名資訊。對於老的MS-DOS FAT 和 一些CDFS 檔案系統來說,這並不可保證,取決於實際的檔名。這種情況下,如果可能的話,短檔名將被取代。
路徑
一個檔案的路徑,包含一個或多個組成部分,被反斜線分割,每一個組成部分代表了一個目錄名或檔名(但有一些意外情況將在下面討論).系統對於路徑的開頭或字首的解釋是十分重要的,這個字首決定了路徑所使用的名稱空間,以及路徑中的哪個位置使用了那些特殊字元,包括最後一個字元。
如果路徑中的一部分是一個檔名,那它必須是最後一個部分。
路徑中的每一個部分都受限於特定的檔案系統的最大長度限制。通常,規則分為兩類:短和長。目錄名被檔案系統以特殊檔案的方式儲存,但是,檔案的命名規則也會應用到目錄名。總的來說,路徑代表了組成路徑的所有的目錄和檔案的一種等級關係。
全路徑和相對路徑
儘管一些API 要求全路徑,windows 中的一些操作檔案的API 支援相對路徑。如果一個檔名不以下面所示的開場,那它將是相對於當前路徑的:
- 以”\\” 開始的UNC 名
- 一個磁碟代號加反斜線,比如“C:”
- 當個反斜線,比如:“\directory” 或 “file.txt”被認為是絕對路徑
如果檔名為“C:” 或者其它磁碟直接加檔名,則,認為是相對檔案。”C:tmp.txt”,指磁碟C: 上的當前目錄下的tmp.txt 檔名。”C:tempdir\tmp.txt” 類似。
如果一個目錄包含”..”,那麼它也被認為是相對路徑。”..”表示當前目錄的上層目錄。
支援如下操作:
“C:..\tmp.txt”
最大路徑長度限制
Windows API 中,最大路徑長度是MAX_PATH(例外情況將單獨討論),定義為260。本地路徑有如下順序的組成部分:
驅動器號,冒號,反斜線,由反斜線分割的名稱組成部分,最後的空字元。
例如:D 盤上的最長的路徑為:
“D:\some256-character path string”
NT 風格的檔名中,Windows API 將”/” 轉換為”\”,以作為部分的檔案路徑。但是使用“\\?\”的使用是例外。不能使用”//?//”
Windows API 中的一些函式擁有Unicode 版本,其可以支援擴充套件長度的路徑,最大可達到32767 字元。這種型別的元件由反斜線分割的組成部分構成,每個部分的長度限制為:GetVolumeInformation 返回的lpMaximumComponentLength 資訊。當需要指定擴充套件長度的路徑,使用“\\?\”字首,比如,“\\?\D:\very long path”。
“\\?\”字首也可以被用於UNC,如果想指定我們的函式就是使用的UNC,可以使用\\?\UNC\字首。這些字首並不是路徑本身的一部分。它們指出,路徑應該儘量少的修改,不可以使用“/”代替”\”,不可以使用”.”代表當前目錄,不可以使用“..”表示父目錄。因為你不能使用”\\?\”字首+相對路徑,相對路徑被限制了MAX_PATH 的長度。
無需對路徑和檔案字串執行任何Unicode 規範化以供windows 檔案I/O API 使用,因為檔案系統將路徑和檔名視為不透明的WCHAR 序列。
使用API 建立目錄時,指定的路徑不能太長,以至於無法附加8.3 檔名(即目錄名不能超過MAX_PATH減去12)
Shell 和 檔案系統的需求不同,使用windows API 是可以建立shell 使用者介面無法解析的路徑的。
從 windows 10,1607 開始,MAX_PATH 限制從普通的win32 檔案和目錄函式中移除了。
一個登錄檔鍵可以控制是否開啟新的長路徑行為。
HKLM\SYSTEM\CurrentControlSet\Control\FileSystem LongPathsEnabled (Type: REG_DWORD)
鍵值在程序第一次呼叫一些win32 檔案或目錄函式後,將快取這個值。
通過組策略將有機會控制這個值。
也可以通過應用程式的manifest 檔案對單個app 配置其行為。
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings xmlns:ws2="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
<ws2:longPathAware>true</ws2:longPathAware>
</windowsSettings>
</application>
下面的這些函式,將沒有MAX_PATH 限制,如果app 選擇接受新的長路徑行為:
CreateDirectoryW,CreateDirectoryExW,GetCurrentDirectoryW,RemoveDirectoryW,SetCurrentDirectoryW。
類似的,檔案管理函式:
CopyFileW, CopyFile2, CopyFileExW, CreateFileW, CreateFile2, CreateHardLinkW, CreateSymbolicLinkW, DeleteFileW, FindFirstFileW, FindFirstFileExW, FindNextFileW, GetFileAttributesW, GetFileAttributesExW, SetFileAttributesW, GetFullPathNameW, GetLongPathNameW, MoveFileW, MoveFileExW, MoveFileWithProgressW, ReplaceFileW, SearchPathW, FindFirstFileNameW, FindNextFileNameW, FindFirstStreamW, FindNextStreamW, GetCompressedFileSizeW, GetFinalPathNameByHandleW.
名稱空間
Win API中使用的,有兩種主要型別的名稱空間分類,NT 名稱空間和Win32 名稱空間。NT 名稱空間被設計為最底層的名稱空間,以承載其它的子系統和名稱空間,比如,Win32 子系統,以及擴充套件的Win32 名稱空間,POSIX 子系統等。之前版本的windows 還定義了幾個預定義的或預留的名字給確定的特殊裝置,比如通訊埠,和預設顯示控制檯,現在它們被稱為NT 裝置名稱空間。
Win32 檔案名稱空間
這些例子是為了讓Win API 使用的,不一定能在Windows Shell 應用程式上,比如windows Explorer 上使用。
對於檔案I/O 操作來說,”\\?\”字首,告訴win API 不要解析字串,直接將之後的字串傳遞給檔案系統。比如,如果系統支援大路徑和檔案命名,使用此種方式來傳遞路徑下去即可。
因為它關閉了自動擴充套件,使用”\\?\”後,可以在路徑名中使用”..”和”.”字元。但並不是所有的I/O API 支援”\\?\”,自行參考MSDN
Win32 裝置名稱空間
\\.\ 字首將訪問win32 裝置名稱空間,這樣可以直接訪問物理磁碟和卷,而不經過檔案系統。可以通過這種方式訪問除了磁碟的很多裝置。
比如,通過”COM1”或者\\.\COM1來訪問COM1,因為COM1-COM9 是NT 名稱空間中預留的,當你想訪問COM56,智慧通過\\.\COM56的方式。
再比如,使用CreateFile 傳入\\.\PhysicalDiskX 或者 “\\.\CdRomX” 。這將允許你透過檔案系統直接訪問這些裝置。這些裝置名時在系統列舉這些裝置的時候建立的。一些驅動也會在系統中建立一些別名。比如,實現名稱“C:\”的裝置驅動程式具有自己的名稱空間,該名稱空間也恰好是檔案系統。
NT 名稱空間
大部分情況下,我們沒有必要通過API 直接操作NT 名稱空間,WinObj 可以比較直觀的瀏覽windows NT 名稱空間。根目錄為”\.”,
子目錄”Global??”就是Win32 名稱空間所在的地方。
命名裝置物件在NT 名稱空間中的位置為“Device”子目錄。
Device 子目錄下,你可能會看到Serial0 和 Serial1,該裝置物件代表了前兩個COM 埠,如果它當前在你係統上。
代表卷的裝置物件看起來應該類似:”HarddiskVolume1”,數字字尾可能不同。
“Harddisk0” 下的”DR0”是代表磁碟的裝置物件的一個例子。
為了讓Win32 API 訪問這些裝置物件,裝置驅動在Win32 名稱空間建立了代表裝置物件的一個符號連結。比如,”Global??”下的COM0 和 COM1簡單的連結到了Serial0 和 Serial1。”C:”被連結到HarddiskVolume1, "Physicaldrive0" 對應” DR0”。
沒有連結的話,任何Windows APP 將無法訪問該裝置。支援NT 名稱空間絕對路徑(比如”\Device\Xxx”)的API 可以獲得一個裝置的控制代碼。
隨著通過終端服務和虛擬機器增加的多使用者支援,在Win32 名稱空間來虛擬化系統範圍的根裝置變得更加有必要。這通過在Win32 名稱空間下新增名為“GLOBALROOT” 的連結來實現。可以使用\\?\GLOBALROOT 訪問,這個字首確保在它後面增加的路徑看起來是在真的系統物件管理器的根路徑下,而不是會話相關的路徑。