Directory.CreateDirectory 因无效字符而失败

Directory.CreateDirectory fails with invalid character

我遇到的问题是我的路径字符串通过了 Path.GetInvalidPathChars() 检查,但在尝试创建目录时失败了。

static void Main(string[] args)
{

    string str2 = @"C:\Temp\hjk&(*&ghj\config\";

    foreach (var character in System.IO.Path.GetInvalidPathChars())
    {
         if (str2.IndexOf(character) > -1)
         {

             Console.WriteLine("String contains invalid path character '{0}'", character);
             return;
         }
    }


    Directory.CreateDirectory(str2); //<-- Throws exception saying Invalid character.

    Console.WriteLine("Press any key..");
    Console.ReadKey();
}

知道可能是什么问题吗?

在这种情况下,文档措辞中的细微问题可能会对我们如何看待或使用 API 产生重大影响。在我们的例子中, API 的一部分对我们没有多大好处。


您还没有完全阅读 Path.GetInvalidPathChars() 上的文档:

The array returned from this method is not guaranteed to contain the complete set of characters that are invalid in file and directory names. The full set of invalid characters can vary by file system. For example, on Windows-based desktop platforms, invalid path characters might include ASCII/Unicode characters 1 through 31, as well as quote ("), less than (<), greater than (>), pipe (|), backspace (\b), null ([=24=]) and tab (\t).

并且不要认为 Path.GetInvalidFileNameChars() 会立即对您有任何好处(我们将在下面证明这是更好的选择):

The array returned from this method is not guaranteed to contain the complete set of characters that are invalid in file and directory names. The full set of invalid characters can vary by file system. For example, on Windows-based desktop platforms, invalid path characters might include ASCII/Unicode characters 1 through 31, as well as quote ("), less than (<), greater than (>), pipe (|), backspace (\b), null ([=24=]) and tab (\t).


在这种情况下,最好try { Directory.CreateDirectory(str2); } catch (ArgumentException e) { /* Most likely the path was invalid */ } 而不是手动验证路径*。这将独立于文件系统工作。


当我试图在我的 Windows 系统上创建您的目录时:

现在,如果我们遍历该数组中的所有字符:

foreach (char c in Path.GetInvalidPathChars())
{
    Console.WriteLine($"0x{(int)c:X4} : {c}");
}

我们得到:

0x0022 : "
0x003C : <
0x003E : >
0x007C : |
0x0000 : 
0x0001 : 
0x0002 : 
0x0003 : 
0x0004 : 
0x0005 : 
0x0006 : 
0x0007 : 
0x0008 : 
0x0009 :   
0x000A : 

0x000B : 
0x000C : 
0x000D : 
0x000E : 
0x000F : 
0x0010 : 
0x0011 : 
0x0012 : 
0x0013 : 
0x0014 : 
0x0015 : 
0x0016 : 
0x0017 : 
0x0018 : 
0x0019 : 
0x001A : 
0x001B : 
0x001C : 
0x001D : 
0x001E : 
0x001F : 

如您所见,该列表不完整。

但是:如果我们对GetInvalidFileNameChars()

做同样的事情
foreach (char c in Path.GetInvalidFileNameChars())
{
    Console.WriteLine($"0x{(int)c:X4} : {c}");
}

我们最终得到了一个不同的列表,其中包括以上所有内容,以及:

0x003A : :
0x002A : *
0x003F : ?
0x005C : \
0x002F : /

正是我们的错误消息所指示的。在这种情况下,您可能会决定改用它。请记住我们上面的警告,Microsoft 不保证这两种方法的准确性。

当然,这并不完美,因为在 路径 上使用 Path.GetInvalidFileNameChars() 会引发错误的无效(\ 在文件名中无效,但它在路径中完全有效!),因此您需要对此进行更正。您可以通过忽略(至少)以下字符来做到这一点:

0x003A : :
0x005C : \

您可能还想忽略以下字符(因为有时人们使用 web/*nix 样式路径):

0x002F : /

这里要做的最后一件事是演示 稍微 更简单的编写此代码的方法。 (我是 Code Review 的常客,所以这是第二天性。)

我们可以一次完成所有事情:

System.IO.Path.GetInvalidFileNameChars().Except(new char[] { '/', '\', ':' }).Count(c => str2.Contains(c)) > 0

用法示例:

var invalidPath = @"C:\Temp\hjk&(*&ghj\config\";
var validPath = @"C:\Temp\hjk&(&ghj\config\"; // No asterisk (*)

var invalidPathChars = System.IO.Path.GetInvalidFileNameChars().Except(new char[] { '/', '\', ':' });

if (invalidPathChars.Count(c => invalidPath.Contains(c)) > 0)
{
    Console.WriteLine("Invalid character found.");
}
else
{
    Console.WriteLine("Free and clear.");
}

if (invalidPathChars.Count(c => validPath.Contains(c)) > 0)
{
    Console.WriteLine("Invalid character found.");
}
else
{
    Console.WriteLine("Free and clear.");
}

*:这是有争议的,如果您确定您的验证代码不会使有效路径无效[=83=,您可能想要手动验证路径].正如 MikeT 所说:"you should always try to validate before getting an exception"。您的验证代码应等于或低于下一级验证的限制。

我遇到了与上述相同的问题。

由于每个子目录名实际上是一个文件名,所以我把我在这里找到的一些解决方案连接起来:

string retValue = string.Empty;
var dirParts = path.Split(Path.DirectorySeparatorChar, (char)StringSplitOptions.RemoveEmptyEntries);

for (int i = 0; i < dirParts.Length; i++)
{
    if (i == 0 && Path.IsPathRooted(path))
    {
        retValue = string.Join("_", dirParts[0].Split(Path.GetInvalidPathChars()));
    } 
    else
    {
        retValue = Path.Combine(retValue, string.Join("_", dirParts[i].Split(Path.GetInvalidFileNameChars())));
    }
}

我的解决方案returns“C:\Temp\hjk&(_&ghj\config”对于问题中给定的路径。