Windows和Linux目录名中禁止使用哪些字符?

我知道/在Linux中是非法的,下面这些在Windows中是非法的 (我想)* . " / \ [ ] : ; | .0

我还遗漏了什么?

我需要一个全面的指南,然而,一个考虑到 双字节字符。链接到外部资源对我来说很好

我需要首先在文件系统上创建一个目录,使用一个可能 包含禁用字符,所以我计划将这些字符替换为 下划线。然后,我需要将这个目录及其内容写入一个zip文件 (使用Java),因此关于zip目录名称的任何其他建议

607845 次浏览
禁止文件名字符的“综合指南”在Windows上不起作用,因为它保留了文件名和字符。是的,像这样的角色 * " ?等名称是禁止使用的,但是还有无数个仅由有效字符组成的名称是禁止使用的。例如,空格和点是有效的文件名字符,但仅由这些字符组成的名称是禁止的 Windows不区分大写字母和小写字母,因此如果已经存在名为a的文件夹,则不能创建名为A的文件夹。更糟糕的是,像PRNCON这样看似允许的名字,以及许多其他名字,都是保留的,不允许的。Windows也有一些长度限制;在一个文件夹中有效的文件名如果移到另一个文件夹中可能会失效。的规则 命名文件和文件夹 都在微软文档上。

一般来说,不能使用用户生成的文本创建Windows目录名。如果您希望允许用户任意命名,则必须创建安全的名称,如AABA2等,将用户生成的名称及其对应路径存储在应用程序数据文件中,并在应用程序中执行路径映射。

如果您绝对必须允许用户生成文件夹名,那么判断它们是否无效的唯一方法是捕获异常并假定名称无效。即使这样也充满了危险,因为为拒绝访问、脱机驱动器和驱动器空间外抛出的异常与为无效名称抛出的异常重叠。你正在打开一个巨大的伤害罐。

好吧,如果只是为了研究目的,那么您最好的选择是查看这个关于文件名的维基百科条目

如果您希望编写一个可移植函数来验证用户输入并基于此创建文件名,那么简单的答案是。看一看像Perl的规范文件:这样的可移植模块,了解一下完成这样一个“简单”任务所需的所有跳转。

在Linux和其他与unix相关的系统下,传统上只有两个字符不能出现在文件或目录的名称中,它们是NUL '\0'和斜杠'/'。当然,斜杠可以出现在路径名中,分隔目录组件。

谣言1说Steven Bourne(因“shell”而出名)有一个包含254个文件的目录,每个字母(字符代码)都可以出现在一个文件名中(不包括/'\0';名称.当然是当前目录)。它被用来测试伯恩外壳,并经常对备份程序等不小心的程序造成严重破坏。

其他 已经介绍了Windows文件名的规则,并链接到微软维基百科

注意MacOS X有一个不区分大小写的文件系统。它的当前版本似乎允许在文件名中使用冒号:,尽管历史上并不一定总是这样:

$ echo a:b > a:b
$ ls -l a:b
-rw-r--r--  1 jonathanleffler  staff  4 Nov 12 07:38 a:b
$

但是,至少在macOS Big Sur 11.7中,文件系统不允许文件名不是有效的UTF-8字符串。这意味着文件名不能由UTF-8中始终无效的字节组成(0xC0, 0xC1, 0xF5-0xFF),并且不能使用延续字节0x80..0xBF作为文件名中的唯一字节。给出的错误是92非法字节序列。

POSIX定义了便携式文件名字符集,包括:

A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
a b c d e f g h i j k l m n o p q r s t u v w x y z
0 1 2 3 4 5 6 7 8 9 . _ -

坚持使用完全由这些字符组成的名称可以避免大部分问题,不过Windows仍然增加了一些复杂性。


1是Kernighan和Pike在['编程实践'](http://www.cs.princeton.edu/~bwk/tpop.webpage/)中说的,在第6章,测试,§6.5压力测试:

当Steve Bourne在编写他的Unix shell(后来被称为Bourne shell)时,他创建了一个包含254个文件的目录,每个文件都有一个字符的名称,除了'\0'和斜杠这两个字符不能出现在Unix文件名中。他将该目录用于模式匹配和标记化的各种测试。(测试目录当然是由程序创建的。)多年以后,这个目录成了文件树漫游程序的祸害;这是对他们的毁灭考验。

注意,目录必须包含...条目,因此它可以说是253个文件(和2个目录),或255个名称条目,而不是254个文件。这并不影响轶事的有效性,也不影响它所描述的仔细测试

TPOP之前在 # EYZ0和 http://cm.bell-labs.com/cm/cs/tpop但是现在(2021-11-12)都坏了。 参见维基百科TPOP < /一口> < / p >

您可以使用白名单,而不是创建字符黑名单。考虑到所有因素,在文件或目录名称上下文中有意义的字符范围非常短,除非您有一些非常特定的命名要求,否则如果用户不能使用整个ASCII表,他们不会反对您的应用程序。

它不能解决目标文件系统中保留名称的问题,但是使用白名单可以更容易地降低源上的风险。

本着这种精神,这是一个可以被认为是安全的字符范围:

  • 字母(a-z a-z) -如果需要,也可以使用Unicode字符
  • 数字(0 - 9)
  • 下划线(_)
  • 连字符(-)
  • 空间
  • 点号(.)

以及您希望允许的任何其他安全字符。除此之外,您只需要强制执行一些关于空格和点的附加规则。这通常就足够了:

  • 名称必须包含至少一个字母或数字(以避免只有点/空格)
  • 名称必须以字母或数字开头(以避免前导点/空格)
  • 名称不能以点或空格结尾(如果存在,只需修剪它们,就像资源管理器一样)

这已经允许相当复杂和无意义的名称。例如,在这些规则下,这些名称是可能的,并且在Windows/Linux中是有效的文件名:

  • # EYZ0
  • # EYZ0

从本质上讲,即使白名单上的角色很少,你仍然应该决定什么是真正有意义的,并相应地验证/调整名称。在我的一个应用程序中,我使用了与上面相同的规则,但去掉了任何重复的点和空格。

  1. 禁止可打印ASCII字符是:

    • < p > Linux / Unix:

        / (forward slash)
      
    • < p >窗口:

        < (less than)
      > (greater than)
      : (colon - sometimes works, but is actually NTFS Alternate Data Streams)
      " (double quote)
      / (forward slash)
      \ (backslash)
      | (vertical bar or pipe)
      ? (question mark)
      * (asterisk)
      
  2. < p > # EYZ0

    如果您的数据来自一个允许不可打印字符的来源,那么就需要检查更多内容。

    • < p > Linux / Unix:

        0 (NULL byte)
      
    • < p >窗口:

        0-31 (ASCII control characters)
      

    虽然在Linux/Unix文件系统下创建文件名中包含控制字符的文件是合法的,对于用户来说处理这样的文件可能是一场噩梦。

  3. < p > # EYZ0

    保留的文件名如下:

    • < p >窗口:

        CON, PRN, AUX, NUL
      COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9
      LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, LPT9
      

      (包括它们自己和任意文件扩展名,例如LPT1.txt)。

  4. < p > # EYZ0

    • < p >窗口:

      文件名不能以空格或点结尾。

    • < p > macOS:

      你没有要求它,但以防万一:冒号:和正斜杠/取决于上下文是不允许的(例如,Finder支持斜杠,终端支持冒号)。# EYZ2

要让Windows告诉你答案,最简单的方法是尝试通过资源管理器重命名文件,并输入反斜杠/作为新名称。Windows会弹出一个消息框,告诉你非法字符的列表。

A filename cannot contain any of the following characters:
\ / : * ? " < > |

Microsoft Docs -命名文件,路径和命名空间-命名约定 . >

我也有同样的需求,正在寻找推荐信或标准推荐信,偶然发现了这条线索。我目前在文件和目录名中应该避免的字符黑名单是:

$CharactersInvalidForFileName = {
"pound" -> "#",
"left angle bracket" -> "<",
"dollar sign" -> "$",
"plus sign" -> "+",
"percent" -> "%",
"right angle bracket" -> ">",
"exclamation point" -> "!",
"backtick" -> "`",
"ampersand" -> "&",
"asterisk" -> "*",
"single quotes" -> "“",
"pipe" -> "|",
"left bracket" -> "{",
"question mark" -> "?",
"double quotes" -> "”",
"equal sign" -> "=",
"right bracket" -> "}",
"forward slash" -> "/",
"colon" -> ":",
"back slash" -> "\\",
"lank spaces" -> "b",
"at sign" -> "@"
};

虽然唯一非法的Unix字符可能是/NULL,但应该考虑到命令行解释。

例如,虽然在Unix中将文件命名为1>&22>&1是合法的,但在命令行中使用这样的文件名可能会被误解。

类似地,也可以将一个文件命名为$PATH,但是当试图从命令行访问它时,shell将把$PATH转换为它的变量值。

在Unix shell中,您几乎可以用'单引号引用所有字符。除了单引号本身之外,您不能表示控制字符,因为\没有展开。从带引号的字符串中访问单引号本身是可能的,因为您可以用单引号和双引号连接字符串,如'I'"'"'m',它可以用于访问一个名为"I'm"的文件(这里也可以使用双引号)。

因此应该避免所有控制字符,因为它们很难在shell中输入。其余部分仍然很有趣,特别是以破折号开头的文件,因为大多数命令将这些文件视为选项,除非前面有两个破折号--,或者使用./指定它们,这也隐藏了开头的-

如果你想要更好,不要使用shell和典型命令使用的任何字符作为语法元素,有时依赖于位置,所以例如,你仍然可以使用-,但不能作为第一个字符;与.相同,只有当你想要它(“隐藏文件”)时,你才能使用它作为第一个字符。如果您是恶意的,您的文件名是VT100转义序列;-),因此ls会使输出乱码。

截至2017年4月18日,这个话题的答案中没有简单的字符和文件名的黑白列表,而且有很多回复。

我能想到的最好的建议是让用户随意命名文件。当应用程序试图保存文件时,使用错误处理程序,捕捉任何异常,假定是文件名造成的错误(显然在确保保存路径也正确之后),并提示用户输入新的文件名。为了获得最好的结果,将这个检查过程放在一个循环中,直到用户正确或放弃为止。对我来说是最好的工作(至少在VBA)。

在Windows中创建internet快捷方式时,为了创建文件名,它会跳过非法字符,除了正斜杠被转换为减号。

对于Windows,您可以使用PowerShell检查它

$PathInvalidChars = [System.IO.Path]::GetInvalidPathChars() #36 chars

要显示您可以转换的UTF-8代码

$enc = [system.Text.Encoding]::UTF8
$PathInvalidChars | foreach { $enc.GetBytes($_) }


$FileNameInvalidChars = [System.IO.Path]::GetInvalidFileNameChars() #41 chars


$FileOnlyInvalidChars = @(':', '*', '?', '\', '/') #5 chars - as a difference

在Windows 10(2019)中,输入以下字符时会出现错误,导致输入失败:

文件名不能包含以下字符:

# EYZ0 # EYZ1

下面是一个基于Christopher Oezbek的回答是的c#实现

containsFolder布尔值使它更加复杂,但希望涵盖所有内容

/// <summary>
/// This will replace invalid chars with underscores, there are also some reserved words that it adds underscore to
/// </summary>
/// <remarks>
/// https://stackoverflow.com/questions/1976007/what-characters-are-forbidden-in-windows-and-linux-directory-names
/// </remarks>
/// <param name="containsFolder">Pass in true if filename represents a folder\file (passing true will allow slash)</param>
public static string EscapeFilename_Windows(string filename, bool containsFolder = false)
{
StringBuilder builder = new StringBuilder(filename.Length + 12);


int index = 0;


// Allow colon if it's part of the drive letter
if (containsFolder)
{
Match match = Regex.Match(filename, @"^\s*[A-Z]:\\", RegexOptions.IgnoreCase);
if (match.Success)
{
builder.Append(match.Value);
index = match.Length;
}
}


// Character substitutions
for (int cntr = index; cntr < filename.Length; cntr++)
{
char c = filename[cntr];


switch (c)
{
case '\u0000':
case '\u0001':
case '\u0002':
case '\u0003':
case '\u0004':
case '\u0005':
case '\u0006':
case '\u0007':
case '\u0008':
case '\u0009':
case '\u000A':
case '\u000B':
case '\u000C':
case '\u000D':
case '\u000E':
case '\u000F':
case '\u0010':
case '\u0011':
case '\u0012':
case '\u0013':
case '\u0014':
case '\u0015':
case '\u0016':
case '\u0017':
case '\u0018':
case '\u0019':
case '\u001A':
case '\u001B':
case '\u001C':
case '\u001D':
case '\u001E':
case '\u001F':


case '<':
case '>':
case ':':
case '"':
case '/':
case '|':
case '?':
case '*':
builder.Append('_');
break;


case '\\':
builder.Append(containsFolder ? c : '_');
break;


default:
builder.Append(c);
break;
}
}


string built = builder.ToString();


if (built == "")
{
return "_";
}


if (built.EndsWith(" ") || built.EndsWith("."))
{
built = built.Substring(0, built.Length - 1) + "_";
}


// These are reserved names, in either the folder or file name, but they are fine if following a dot
// CON, PRN, AUX, NUL, COM0 .. COM9, LPT0 .. LPT9
builder = new StringBuilder(built.Length + 12);
index = 0;
foreach (Match match in Regex.Matches(built, @"(^|\\)\s*(?<bad>CON|PRN|AUX|NUL|COM\d|LPT\d)\s*(\.|\\|$)", RegexOptions.IgnoreCase))
{
Group group = match.Groups["bad"];
if (group.Index > index)
{
builder.Append(built.Substring(index, match.Index - index + 1));
}


builder.Append(group.Value);
builder.Append("_");        // putting an underscore after this keyword is enough to make it acceptable


index = group.Index + group.Length;
}


if (index == 0)
{
return built;
}


if (index < built.Length - 1)
{
builder.Append(built.Substring(index));
}


return builder.ToString();
}

讨论不同的可能方法

定义困难,什么是合法的什么是不合法的 都已经讲过了和白名单提出了。但不仅是Windows,还有许多unix操作系统支持more-than-8-bit字符,如Unicode。您还可以在这里讨论诸如utf - 8之类的编码。你可以考虑Jonathan Leffler的评论,在那里他提供了关于现代Linux和描述MacOS的详细信息的信息。维基百科的州,那(例如)

修饰符字母结肠 [(见7。下面)是]有时在Windows文件名中使用,因为它与用于文件名的Segoe UI字体中的冒号相同。[继承的ASCII]冒号本身是不允许的。

因此,我想提出一种更自由的方法,使用Unicode Homoglyph字符来取代“非法”字符。的人。我发现,在我可比的用例中,结果可读性要强得多,它只受限于使用的字体,非常广泛,Windows默认为3903个字符。此外,您甚至可以从替换恢复原始内容。

可能的选择和研究笔记

为了保持内容的组织性,我将始终给出字符,它的名称和十六进制数表示。后者不区分大小写,前导零可以随意添加或省略,因此,例如U+002Au+2a是等效的。如果可用,我会尽量指出更多的信息或替代品-请随时向我展示更多或更好的。

  1. 代替 (U+2A * ASTERISK),你可以使用列出的众多中的一个,例如U+2217 ∗ (ASTERISK OPERATOR)Full Width Asterisk U+FF0A *u+20f0 ⃰ combining asterisk above将变音符符号组合为符号可能也是一个有效的选择。你可以读4。有关结合字符的更多信息。

  2. 而不是 (U+2E . full stop), 这些中的一个可能是一个很好的选择,例如⋅ U+22C5 dot operator

  3. 代替“; (U+22 " quotation mark),你可以使用“ U+201C english leftdoublequotemark,更多的替代品在这里看到的。我还包括了沃利·布罗克韦的回答的一些好的建议,在这种情况下,u+2036 ‶ reversed double primeu+2033 ″ double prime -我将从现在开始用¹³来表示来自该来源的想法。

  4. 而不是∕ DIVISION SLASH U+22150 (U+2F / SOLIDUS),你可以使用∕ DIVISION SLASH U+2215(其他∕ DIVISION SLASH U+22151), ̸ U+0338 COMBINING LONG SOLIDUS OVERLAY̷ COMBINING SHORT SOLIDUS OVERLAY U+0337u+2044 ⁄ fraction slash∕ DIVISION SLASH U+22152。注意一些字符的间距,包括combiningoverlay字符,因为它们没有宽度,可以产生-≯th̷是̸th̷is。加上空格,得到-≯th̷是,即̸ th ̷ is。第二个(COMBINING SHORT SOLIDUS OVERLAY)在stackoverflow-font中看起来很糟糕。

  5. 你可以使用⧵ U+29F5 Reverse solidus operator (更多的)或u+20E5 ⃥ combining reverse solidus overlay¹³,而不是\ (U+5C Reverse solidus)。

  6. 要替换 (U+5B [ Left square bracket)和 (U+005D ] Right square bracket),您可以使用例如U+FF3B[ FULLWIDTH LEFT SQUARE BRACKETU+FF3D ]FULLWIDTH RIGHT SQUARE BRACKET(从在这里,更多的可能性在这里)。

  7. 代替 (u+3a : colon),你可以使用U+2236 ∶ RATIO (for mathematical usage)U+A789 ꞉ MODIFIER LETTER COLON,(参见结肠(信),有时在Windows文件名中使用,因为它与用于文件名的Segoe UI字体中的冒号相同。冒号本身是不允许的……来源和更多的替换见在这里)。另一种选择是:u+1361 ፡ ethiopic wordspace¹³

  8. 代替 (u+3b ; semicolon),你可以使用U+037E ; GREEK QUESTION MARK(见在这里)。

  9. 对于| (u+7c | vertical line),有一些很好的替代品,如:U+2223 ∣ DIVIDESU+0964 । DEVANAGARI DANDAU+01C0 ǀ LATIN LETTER DENTAL CLICK(来自维基百科的最后一个)或U+2D4F ⵏ Tifinagh Letter Yan。此外,方框绘图字符包含各种其他选项。

  10. 而不是 (, U+002C COMMA),你可以使用例如‚ U+201A SINGLE LOW-9 QUOTATION MARK(见在这里)。

  11. 对于? (U+003F ? QUESTION MARK),这些是很好的候选:U+FF1F ? FULLWIDTH QUESTION MARKU+FE56 ﹖ SMALL QUESTION MARK(从在这里在这里)。还有两个来自愚蠢的块(搜索“问题”)和u+203d ‽ interrobang¹³

  12. 虽然我的机器似乎接受它不变,我仍然想包括> (u+3e greater-than sign)和< (u+3c less-than sign)为了完整性。这里最好的替代品可能也来自u+3e greater-than sign0,比如u+203a › single right-pointing angle quotation marku+2039 ‹ single left-pointing angle quotation mark。tifinagh块只包含ⵦ (u+2D66)u+3e greater-than sign1来替换<。最后一个概念是⋖ less-than with dot u+22D6⋗ greater-than with dot u+22D7

对于更多的想法,您还可以查看例如这一块。你还想要更多的想法吗?你可以试着画出你想要的角色,看看这里的建议

你怎么打这些字符

假设您想要键入ⵏ (Tifinagh Letter Yan)。为了获得它的所有信息,你可以总是在一个合适的平台上搜索这个字符(),比如这个Unicode查找(当你搜索十六进制时请添加0x)或Unicode表(只允许搜索名字,在这种情况下"Tifinagh Letter Yan")您应该获得它的Unicode编号U+2D4F和html代码&#11599; (注意#EYZ5对于11599来说是十六进制的)。有了这些知识,您就有几个选项来生成这些特殊字符,包括使用

  • 代码指向unicode转换器Unicode查找将数值表示形式反向转换为unicode字符(请记住将下面的码位基分别设置为十进制或十六进制)
  • 一行makro在Autohotkey: :?*:altpipe::{U+2D4F}而不是字符串altpipe -这是我输入这些特殊字符的方式,如果有共同的兴趣,我的自动热键脚本可以共享
  • Alt字符或alt代码,按下并按住alt,然后是所需字符的十进制数字(更多信息例如在这里,查看表在这里在那里)。例如:Alt+11599。请注意,许多程序并不完全支持所有unicode的此windows特性(截至本文撰写时)。alt1icroalt2oft Office是一个例外,它通常工作,其他一些操作系统提供类似的功能。用alt组合输入这些字符到MS Word中也是alt3在已经提到的alt4中建议的方式-如果你不想将所有十六进制值转换为alt5,你可以找到其中的一些alt6。
  • 在MS Office中,您也可以使用ALT + X(如这篇微软文章所述)来生成字符
  • 如果你很少需要它,当然你仍然可以复制粘贴你选择的特殊字符,而不是输入它

对于任何寻找正则表达式的人:

const BLACKLIST = /[<>:"\/\\|?*]/g;

我一直以为Windows文件名中禁用字符意味着所有外来字符也将被禁止。无法使用?/尤其让我感到恼火。有一天,我发现实际上只有那些字符是被禁止的。可以使用其他Unicode字符。因此,我能找到的最接近禁用字符的Unicode字符被识别出来,并为它们制作了MS Word宏,如Alt+?Alt+等。现在我在Word中使用替换字符形成文件名,并将其复制到Windows文件名中。到目前为止我还没有遇到任何问题。

下面是替换字符(Alt +十进制Unicode):

  • ⃰⇔# eyz08432
  • ⁄# eyz08260
  • ⃥⇔# eyz08421
  • ∣z08739
  • ⵦ⇔ayz011622
  • ⮚⇔⇔
  • 配置成功
  • ፡⇔⇔
  • ‶⇔# eyz08246
  • ″⇔# eyz08243

作为测试,我用所有这些字符组成了一个文件名,Windows接受了它。

这对我来说在Python中已经足够好了:

def fix_filename(name, max_length=255):
"""
Replace invalid characters on Linux/Windows/MacOS with underscores.
List from https://stackoverflow.com/a/31976060/819417
Trailing spaces & periods are ignored on Windows.
>>> fix_filename("  COM1  ")
'_ COM1 _'
>>> fix_filename("COM10")
'COM10'
>>> fix_filename("COM1,")
'COM1,'
>>> fix_filename("COM1.txt")
'_.txt'
>>> all('_' == fix_filename(chr(i)) for i in list(range(32)))
True
"""
return re.sub(r'[/\\:|<>"?*\0-\x1f]|^(AUX|COM[1-9]|CON|LPT[1-9]|NUL|PRN)(?![^.])|^\s|[\s.]$', "_", name[:max_length], flags=re.IGNORECASE)

也可以参阅这个过时的列表以了解FAT32中的=等其他遗留内容。