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

时间:2009-12-29 18:11:36

标签: windows linux directory zip filenames

我知道/在Linux中是非法的,以下在Windows中是非法的 (我认为)* . " / \ [ ] : ; { {1}} | =

我还缺少什么?

然而,我需要一份全面的指南,并考虑到这一指南 双字节字符。与我联系外部资源很好。

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

18 个答案:

答案 0 :(得分:367)

让我们保持简单并首先回答这个问题。

  1. 禁止可打印的ASCII字符为:

    • 的Linux / Unix:

      / (forward slash)
      
    • 视窗:

      < (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. 不可打印字符

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

    • 的Linux / Unix:

      0 (NULL byte)
      
    • 视窗:

      0-31 (ASCII control characters)
      

    注意:虽然在Linux / Unix文件系统下合法创建文件名中包含控制字符的文件it might be a nightmare for the users to deal with such files

  3. 保留文件名

    保留以下文件名:

    • 视窗:

      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. 其他规则

    • 视窗:

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

答案 1 :(得分:185)

禁用文件名字符的“综合指南”在Windows上不起作用,因为它保留了文件名和字符。是的,人物喜欢 * " ?和其他人是被禁止的,但是有无数个名称仅由禁止使用的有效字符组成。例如,空格和点是有效的文件名字符,但禁止仅由这些字符组成的名称。

Windows不区分大写字符和小写字符,因此如果已存在名为A的文件夹,则无法创建名为a的文件夹。更糟糕的是,看似允许的名称,如PRNCON,以及许多其他名称,都是保留的,不允许使用。 Windows也有几个长度限制;如果移动到另一个文件夹,在一个文件夹中有效的文件名可能会无效规则 naming files and folders 在MSDN上。

通常,您不能使用用户生成的文本来创建Windows目录名称。如果您想允许用户命名他们想要的任何内容,您必须创建安全的名称,如AABA2等,存储用户生成的名称及其路径等价物应用程序数据文件,并在您的应用程序中执行路径映射。

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

答案 2 :(得分:57)

在Linux和其他与Unix相关的系统中,只有两个字符不能出现在文件或目录的名称中,而且这些字符是NUL '\0'和斜杠'/'。当然,斜杠可以出现在路径名中,将目录组件分开。

Rumor 1 认为Steven Bourne('shell'成名)有一个包含254个文件的目录,每个字母(字符代码)可以出现一个文件名(不包括{ {1}},/;名称'\0'当然是当前目录。它被用来测试Bourne shell,并经常对诸如备份程序之类的粗心计划造成严重破坏。

其他人已经涵盖了Windows规则。

请注意,MacOS X具有不区分大小写的文件系统。

<小时/> 1 这是Kernighan&amp; The Practice of Programming中的派克在第6章,测试,§6.5压力测试中说了很多:

  

当Steve Bourne编写他的Unix shell(后来被称为Bourne shell)时,他创建了一个包含一个字符名称的254个文件的目录,每个字节值除了.和斜杠, Unix文件名中不能出现的两个字符。他使用该目录进行模式匹配和标记化的各种测试。 (测试目录当然是由程序创建的。)多年后,该目录是文件树行走程序的祸根;它测试了它们的破坏。

答案 3 :(得分:31)

您可以使用白名单,而不是创建字符黑名单。考虑到所有因素,在文件或目录名称上下文中有意义的字符范围很短,除非您有一些非常具体的命名要求,否则如果用户不能使用整个ASCII表,则用户不会将其保留在应用程序中。 / p>

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

本着这种精神,这是一系列可以被认为是安全的角色:

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

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

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

这已经允许非常复杂和荒谬的名字。例如,这些名称可以使用这些名称,并且是Windows / Linux中的有效文件名:

  • A...........ext
  • B -.- .ext

从本质上讲,即使白名单字符很少,您仍然应该确定实际有意义的内容,并相应地验证/调整名称。在我的一个应用程序中,我使用了与上面相同的规则,但剥离了任何重复的点和空格。

答案 4 :(得分:27)

好吧,如果仅用于研究目的,那么最好的办法是查看this Wikipedia entry on Filenames

如果你想编写一个便携式功能来验证用户输入并根据它创建文件名,那么简短的回答是不要。看看像Perl File::Spec这样的便携式模块,可以一瞥完成这种“简单”任务所需的所有跳跃。

答案 5 :(得分:24)

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

2015-09-14T13:03:52.225525+00:00 heroku[web.1]: Starting process with command `node app.js`
2015-09-14T13:03:52.271972+00:00 heroku[web.1]: Stopping all processes with SIGTERM
2015-09-14T13:03:54.315461+00:00 heroku[web.1]: Process exited with status 143
2015-09-14T13:03:54.701499+00:00 app[web.1]: module.js:338
2015-09-14T13:03:54.701522+00:00 app[web.1]:     throw err;
2015-09-14T13:03:54.701523+00:00 app[web.1]:           ^
2015-09-14T13:03:54.701525+00:00 app[web.1]: Error: Cannot find module 'mongodb-core'
2015-09-14T13:03:54.701526+00:00 app[web.1]:     at Function.Module._resolveFilename (module.js:336:15)
2015-09-14T13:03:54.701527+00:00 app[web.1]:     at Function.Module._load (module.js:278:25)
2015-09-14T13:03:54.701529+00:00 app[web.1]:     at require (module.js:384:17)
2015-09-14T13:03:54.701528+00:00 app[web.1]:     at Module.require (module.js:365:17)
2015-09-14T13:03:54.701531+00:00 app[web.1]:     at Object.<anonymous> (/app/node_modules/mongodb/index.js:2:12)
2015-09-14T13:03:54.701532+00:00 app[web.1]:     at Module._compile (module.js:460:26)
2015-09-14T13:03:54.701533+00:00 app[web.1]:     at Object.Module._extensions..js (module.js:478:10)
2015-09-14T13:03:54.701534+00:00 app[web.1]:     at Module.load (module.js:355:32)
2015-09-14T13:03:54.701535+00:00 app[web.1]:     at Function.Module._load (module.js:310:12)
2015-09-14T13:03:54.701536+00:00 app[web.1]:     at Module.require (module.js:365:17)
2015-09-14T13:03:55.561071+00:00 heroku[web.1]: State changed from starting to crashed
2015-09-14T13:03:55.548974+00:00 heroku[web.1]: Process exited with status 1

https://support.microsoft.com/en-us/kb/177506

答案 6 :(得分:6)

定义{em>合法与否的困难是already adressed和白名单were suggested。但是Windows 支持超过8位的字符Wikipedia states,例如

修饰语字母colon [(请参阅下面的7。)有时用于Windows文件名,因为它与用于文件名的Segoe UI字体中的冒号相同。不允许使用[继承的ASCII]冒号。

因此,我想提出一种使用Unicode字符代替“非法”字符的更为自由的方法。我在类似的用例中发现了结果,可读性更高。例如查看this block。另外,您甚至可以从中还原原始内容。以下列表提供了可能的选择和研究:

  1. 代替 * U+002A * ASTERISK),您可以使用列出的one of the many,例如U+2217 ∗ (ASTERISK OPERATOR)Full Width Asterisk U+FF0A *
  2. 代替,您可以使用these中的一个,例如⋅ U+22C5 dot operator
  3. 您可以使用“ U+201C english leftdoublequotemark(替代参见here)来代替
  4. 您可以使用/ SOLIDUS U+002F(其他here)来代替 / ∕ DIVISION SLASH U+2215
  5. 您可以使用\ U+005C Reverse solidusmore)代替 \ ⧵ U+29F5 Reverse solidus operator
  6. 例如,可以使用U+005B Left square bracket和{{代替 [U+005D Right square bracket)和] U+FF3B[ FULLWIDTH LEFT SQUARE BRACKET)。 1}}(来自here,更多可能性here
  7. 您可以使用U+FF3D ]FULLWIDTH RIGHT SQUARE BRACKETU+2236 ∶ RATIO (for mathematical usage)来代替(请参阅colon (letter),有时在Windows文件名中使用,因为它与用于文件名的Segoe UI字体。不允许冒号本身)(请参见here
  8. 您可以使用U+A789 ꞉ MODIFIER LETTER COLON(请参见here)来代替;
  9. 对于 | ,有一些很好的替代品,例如:U+037E ; GREEK QUESTION MARKU+0964 । DEVANAGARI DANDAU+2223 ∣ DIVIDESWikipedia)。 box drawing characters还包含其他各种选项。
  10. 例如,您可以使用U+01C0 ǀ LATIN LETTER DENTAL CLICK(请参见here)代替, U+002C COMMA
  11. 对于‚ U+201A SINGLE LOW-9 QUOTATION MARK),这些都是不错的候选者:U+003F ? QUESTION MARKU+FF1F ? FULLWIDTH QUESTION MARK(来自he re,两个来自Dingbats Block的更多信息,搜索“问题”)

答案 7 :(得分:5)

对于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

答案 8 :(得分:3)

在Windows 10(2019)中,尝试输入以下字符时会出现错误:

  

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

     

\ / : * ? " < > |

答案 9 :(得分:2)

截至2017年4月18日,本主题的答案中没有明显的黑色或白色字符和文件名列表 - 并且有很多回复。

我能提出的最好的建议是让用户为他喜欢的文件命名。当应用程序尝试保存文件时使用错误处理程序,捕获任何异常,假设文件名是责备(显然确保保存路径也正常),并提示用户输入新文件名。为了获得最佳效果,请将此检查过程置于循环中,直到用户正确或放弃为止。对我来说效果最好(至少在VBA中)。

答案 10 :(得分:2)

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

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

答案 11 :(得分:1)

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

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

同样可以命名文件$PATH,但是当尝试从命令行访问它时,shell会将$PATH转换为其变量值。

答案 12 :(得分:1)

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

这里是替代字符(Alt + 十进制 Unicode):

  • ⃰ ⇔ Alt8432
  • ⁄ ⇔ Alt8260
  • ⃥ ⇔ Alt8421
  • ∣ ⇔ Alt8739
  • ⵦ ⇔ Alt11622
  • ⮚ ⇔ Alt11162
  • ‽ ⇔ Alt8253
  • ፡ ⇔ Alt4961
  • ‶ ⇔ Alt8246
  • ″ ⇔ Alt8243

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

答案 13 :(得分:0)

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

答案 14 :(得分:0)

这是基于Christopher Oezbek答案的Windows 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();
}

答案 15 :(得分:0)

.NET Framework System.IO为无效的文件系统字符提供以下功能:

这些功能应该返回适当的结果,具体取决于运行.NET运行时的平台。

答案 16 :(得分:-1)

在Unix shell中,您几乎可以引用单引号'中的每个字符。除单引号本身外,您无法表达控制字符,因为\未展开。可以从带引号的字符串中访问单引号本身,因为您可以使用单引号和双引号连接字符串,例如'I'"'"'m',可用于访问名为"I'm"的文件(此处也可以双引号) )。

所以你应该避免使用所有控制字符,因为它们很难进入shell。其余的仍然很有趣,尤其是以破折号开头的文件,因为大多数命令都将这些命令视为选项,除非您之前有两个破折号--,或者您使用./指定它们,这也隐藏了起始- 1}}。

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

答案 17 :(得分:-6)

我有同样的需求,正在寻找推荐或标准参考,并遇到了这个主题。我目前在文件和目录名称中应避免使用的黑名单是:

$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" -> "@"
};