Windows所支持的所有文件系统都使用文件和目录的概念来访问存储在磁盘或设备上的数据。使用文件及设备I/O类Windows API的开发人员必须了解文件和目录命名的各种规范、约定和限制。 使用文件I/O类API可以访问来自磁盘、设备以及网络上的数据。文件和目录均有一个命名空间,它是路径的一个组成部分。而路径则是一个字符串,它表示到哪里去获取数据,并且不关心“哪里”具体是硬盘、设备还是网络。 有些文件系统,例如NTFS,支持文件和目录链接,这些链接的文件和目录与其它普通文件和目录遵循一样的命名规则。 一、文件名和目录名 所有文件系统对一个独立的文件遵循同样的一般命名规范:一个基本的文件名加一个可选的扩展后缀名,它们之间用"."分隔。但是每个文件系统,如NTFS、CDFS、exFAT、UDFS、FAT和FAT32,对于一个文件(或目录)路径的各组成部分的形式都有一些特殊的不同的规定。注意!目录也只是一个简单的文件,由一个特殊的属性指定它是目录,但也必须遵循文件的命名规则。因为术语“目录”简单来说是指文件系统所关注的一类特殊文件,所以有些资料上使用术语“文件”来表示目录及数据文件本身。因此,如未特殊说明,任何适合于文件的命名、使用规则和例子也适合于目录。术语“路径”由一个或多个目录、反斜杠,也可能包括一个卷名,组成。更多信息参考“路径”一节。 字符数限制的不同取决于不同文件系统及路径所使用的前缀。之所以如此复杂是因为它需要向后兼容。例如,早期的MS-DOS FAT文件系统支持最长8个字符的基本文件名和3个字符的扩展后缀名,包括"."分隔符一共12个字符。这就是熟知的“8.3形式文件名”。Windows FAT和NTFS文件系统则不受“8.3形式文件名”的限制,因为它们支持长文件名,但它们同样支持“8.3形式文件名”。 二、命名规范 下面的基本规则可以使应用程序为文件和目录创建及处理合法的名字,并且是与具体的文件系统无关的: 1.目录或文件的名字中用"."分隔基本的文件名和扩展后缀名 2.用反斜杠分隔路径的各组成部分。反斜杠将文件名从路径中分隔开,也分隔出了目录与目录之间的层次。你不能在文件名和目录名中使用反斜杠,因为它是用来分隔路径各组成部分的保留字符。 3.反斜杠是卷名中必须使用的,例如,"C:\path\file"中的"C:\",UNC命名方式"\\server\share\path\file"中的"\\server\share"。更多关于UNC的信息参考“路径最长长度限制”一节。 4.不要假定名字是大小写敏感的。例如,OSCAR、Oscar和oscar是一样的,尽管有些文件系统(例如POSIX兼容的文件系统)把它们区别对待。注意!NTFS是支持POSIX大小写敏感的标准的,只不过这不是它的默认设置。 5.卷指示符(驱动器字母)也是大小写不敏感的。例如,"D:\"和"d:\"都指示同一个卷。 6.名字中可以使用当前代码页中的任何字符、Unicode字符以及扩展的ASCII字符(128-255),但以下字符除外: <, >, :, ", /, \, |, ?, * 整数0,有时候被当作ASCII的NUL字符。 整数1-31,除非是在替换的数据流中使用。 其它一些目标文件系统不允许使用的字符。 7.在路径中使用"."作为目录时,它代表的是当前目录。例如,".\temp.txt"。更多信息参考“路径”一节。 8.在路径中使用".."作为目录时,它代表的是当前目录的父目录。例如,"..\temp.txt"。更多信息参考“路径”一节。 9.不要使用下面这些保留的设备名作为文件名: CON, PRN, AUX, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, LPT9. 同时也要避免使用这些名称加扩展后缀名的形式,例如,NUL.txt是不推荐使用的。更多信息参考“命名空间”一节。 10.文件名和目录名不能以" "和"."结尾。尽管底层的文件系统是支持这样命名的,但Windows shell和用户接口不允许。但是"."作为名字的第一个字符是可以的,例如".temp"。 三、短/长名字 长文件名是优于短的MS-DOS形式的命名的。通常,Windows将长文件名存在磁盘上作为一个特殊的目录的项,但这也可能因为具体的文件系统由于性能的原因禁止。当你创建一个长文件名时,Windows也创建了一个“8.3形式文件名”,称为“8.3别名”,也存在磁盘上。“8.3别名”也可以在指定的卷上禁止。 Windows Server 2008, Windows Vista, Windows Server 2003, and Windows XP: “8.3别名”无法在指定卷上禁止,Windows 7和Windows Server 2008 R2以后才可以。 在很多文件系统上,路径中文件(目录)名的组成部分可能会包含一个波浪字符(~),这是因为各组成部分的名字太长,无法满足“8.3别名”命名规则。 注意!不是所有的文件系统都遵循用波浪字符替代的约定。即使文件系统通常支持“8.3别名”,但也可能禁止使用。因此,不要假定“8.3别名”已经存在磁盘上了。 需要“8.3形式文件名”、长文件名或一个文件的全路径时可以考虑以下方式: 通过长文件名获取“8.3形式文件名”使用GetShortPathName 通过短文件名获取长文件名使用GetLongPathName 获取文件的全路径使用GetFullPathName 近来的文件系统,如NTFS、exFAT、UDFS及FAT32,Windows存在磁盘上的长文件名使用的是Unicode,这意味着原始的长文件名已经“加工”保存好了。这样,即使一个长文件名包括ASCII扩展字符、无论代码页是否是激活的,都可以正确的读写。 使用长文件名的文件在NTFS和Windows FAT间拷贝不会丢失任何文件名信息。但在老的MS-DOS FAT和一些CDFS(CD-ROM)文件系统间可就不一定了。这种情况下短文件名可能会被替换。 四、路径 指明一个文件(目录)的路径包括一个或多个组成部分,通过反斜杠分隔,每个组成部分通常是一个目录名或文件名。前缀决定了路径所使用的命名空间。 如果文件名是一个路径的组成部分,那么它肯定是最后一部分。 一个特定的文件系统对路径的每个组成部分都有一个最长长度的限制。通常,这些限制分为两大类:短名字和长名字。注意!目录名会被文件系统当作一个特殊的文件存储,文件的命名规则同样适用于目录。总结起来路径就是一个字符串,它代表了一个特定文件或目录与其他目录的层次关系。 五、绝对路径与相对路径 一些文件操作的Windows API使用文件名时通常相对于当前目录,同时也有些API要求使用绝对路径。如果一个文件名不以下面的一种形式开始,那么它就是相对于当前目录的: 任何形式的UNC名字,总是以连续两个反斜杠开始(\\)。更多信息参考下一节。 一个磁盘指示符加一个反斜杠,例如,"C:\"或"d:\"。 一个单独的反斜杠,例如,"\directory"或"\file.txt",这也是绝对路径。 如果一个文件名以一个磁盘指示符开始,但冒号后面没有反斜杠,那么它将被解释为在该驱动器下相对于当前目录的路径。注意!当前目录可能是也可能不是根目录,这取决于经常使用的"cd"命令如何设置了。例如: "C:tmp.txt"是指在C盘当前目录中的"tmp.txt"文件 "C:tempdir\tmp.txt"是指在C盘当前目录的子目录"tempdir"中的"tmp.txt"文件 如果在路径的一个组成部分中包含"..",那么它指示当前目录的父目录。例如: "..tmp.txt"是指当前目录的父目录中的"tmp.txt"文件 "..\..\tmp.txt"是指当前目录的父目录的父目录中的"tmp.txt"文件 "..\tempdir\tmp.txt"是指当前目录的父目录中的"tempdir"中的"tmp.txt"文件 相对路径还可以组合以上例子中的类型,例如"C:..\tmp.txt"。 六、路径最长长度限制 在Windows API中,一个路径的最长长度是MAX_PATH,它被定义为260个字符。一个本地路径由以下部分组成:驱动器字母、冒号、反斜杠、用反斜杠分隔开的各名字的组成部分、NULL结束符。例如,D盘最长的路径为"D:\some 256-character path string",""代表当前系统代码页中的不可见NULL结束符。 注意!Windows API中的文件I/O函数会将名字中的'/'替换成'\'以符合NT风格的名字,但使用"\\?\"前缀时除外。 Windows API中的很多Unicode版本函数允许一个扩展了长度的路径,最长长度可达32,767个字符。这类路径由被反斜杠分隔开的各部分组成,每部分最长可达GetVolumeInformation函数lpMaximumComponentLength参数返回的值(通常这个值是255个字符)。指定一个扩展了长度的路径要使用"\\?\"前缀。例如"\\?\D:\very long path"。 注意!最长路径可达32,767个字符是一个近似值,因为系统运行时可能会将"\\?\"前缀扩展为一个更长的字符串,而这个扩展的长度也会加入总长度中。 "\\?\"前缀也可以用在使用了UNC的路径上。指定一个这样的路径要使用"\\?\UNC\"前缀。例如,"\\?\UNC\server\share\",其中的server是计算机名,share是共享文件夹名。这些前缀不是路径本身的一部分,它们意味着传给系统的路径要有些额外要求——你不能用斜杠作为路径的分隔符,或者用一个"."代表当前目录,或者用".."代表父目录。由于你不能在一个相对路径中使用"\\?\"前缀,因此相对路径总是被限制为MAX_PATH个字符。 使用Windows文件I/O API时,没有必要对路径和文件名字符串执行Unicode化,因为文件系统将路径和文件名看作一个不透明的WCHARs序列。 当使用API创建一个目录时,路径不能太长以至于无法追加一个“8.3形式文件名”(即目录名不能超过MAX_PATH - 12)。 Shell和文件系统有不同的需求,因此通过Windows API创建的一个路径可能在shell下无法正确解释。 七、命名空间 Windows API中主要有两类命名空间约定,NT命名空间和Win32命名空间。NT命名空间被设计为最底层的命名空间以容纳其它子系统及命名空间,包括Win32子系统、Win32命名空间。POSIX是另一个建立在NT命名空间之上的Windows子系统的例子。早期的Windows为一些设备预定义或预留了一些名字,例如串口、并口,它们现在是NT设备命名空间中的了。 八、Win32文件命名空间 Win32命名空间的前缀、约定以及如何使用将在本节和下节介绍完毕。注意!下面的例子是为使用Windows API准备的,不一定适用于Windows shell程序,如Windows Explorer。因此路径的可能形式就比仅适用于Windows shell的路径形式多多了,利用了这点优势的Windows应用程序就可以使用这些命名空间约定来开发。 对于文件I/O来说,一个带有"\\?\"前缀的路径告诉Windows API禁止所有的字符串解析,直接将它传递给文件系统。例如,如果文件系统支持长路径和长文件名,你可以超过MAX_PATH的限制,而这个限制是会在Windows API中强制检查的。 它关闭了路径字符串的自动扩展,因此"\\?\"前缀允许在路径名字中使用".."和".",但它们不代表父目录和当前目录。如果你试图操作某个文件,其绝对路径中包含这些为相对路径保留的指示符的情况时,它就很有用了。 大多数但不是全部文件I/O API支持"\\?\"前缀,你需要看具体API的参考来确定。 九、Win32设备命名空间 "\\.\"前缀可以访问Win32设备命名空间,而不是Win32文件命名空间了。如果API支持的话,它可以直接访问物理磁盘和卷,而非通过文件系统。通过这种方式你可以访问很多设备而不只是磁盘(使用CreateFile和DefineDosDevice)。 例如,你想打开第一个串口,你可以在CreateFile中使用COM1。由于COM1-COM9是NT命名空间中的保留名字,因此是否使用"\\.\"前缀都可以。为了比较,假设你有一块拥有100个串口的扩展板,并且你想打开COM56,你不能使用COM56来打开它,因为在NT命名空间中并没有COM56的预定义。你要使用"\\.\COM56"来打开它,因为"\\.\"前缀会直接去设备命名空间找,而非找一个预定义的别名。 另一个使用Win32设备命名空间的例子是CreateFile "\\.\PhysicalDiskX"(X是一个整数)或"\\.\CdRomX"。它允许你直接访问设备,而非通过文件系统。这个调用能成功是因为系统在枚举设备时创建了这些设备名,有些驱动也会在系统中创建其它别名。例如,实现了"C:\"的设备驱动有它自己的命名空间,文件系统正是这样。 通常与CreateFile配合使用的API都支持"\\.\"前缀,因为CreateFile根据参数的使用既可以打开文件也可以打开设备。 如果你使用Windows API仅能用"\\.\"前缀访问设备,而非文件! 大多数API不支持"\\.\"前缀,只有那些设计来工作在设备命名空间的函数才能识别它。 十、NT命名空间 也有API允许使用NT命名空间的约定,但Windows对象管理器的存在使得在大多数情况下没有这种需求。例如,当使用WinObj在系统对象浏览器中浏览Windows命名空间时就很有用。当你运行这个工具时,根目录或"\"及以下的就是NT命名空间。它下面的"Global??"子目录就是Windows命名空间。命名的设备对象在NT命名空间的"Device"子目录中。这里你将看到Serial0和Serial1,这两个设备对象代表COM1和COM2,当然你的系统上得有COM0和COM1口。代表一个卷的设备对象类似"HarddiskVolume1",这里的数字后缀是可变的。子目录"Harddisk0"下的"DR0"代表一个硬盘。 为了让Windows应用也能访问到这些设备对象,设备驱动在Win32命名空间"Global??"中创建了一个符号链接来代表这些设备对象。例如"Global??"子目录下的COM0、COM1就是Serial0、Serial1简单的链接,"C:"是HarddiskVolume1的链接,"Physicaldrive0"是DR0的链接,等等。没有链接,一个指定的"Xxx"设备将无法被任何Windows应用通过Win32命名空间的约定打开,但是设备句柄可以用那些支持形如"\Device\Xxx"的NT命名空间绝对路径的API打开。 通过终端服务和虚拟机的多用户支持,将需要在Win32命名空间中虚拟出一个系统范围的根设备。这通过在"Global??"中增加一个名为"GLOBALROOT"的链接来实现,并可以通过"\\?\GLOBALROOT\"路径来访问。加上这个前缀的路径看起来就像真的在系统对象管理器的根目录下,而非一个根据会话隔离的路径。