【问题标题】:Sanitizing a file path in C# without compromising the drive letter在不影响驱动器号的情况下清理 C# 中的文件路径
【发布时间】:2019-05-17 17:05:33
【问题描述】:

我需要在 C# 中处理一些可能包含非法字符的文件路径,例如:

C:\path\something\output_at_13:26:43.txt

在那个路径中,时间戳中的:s 使文件名无效,我想用另一个安全字符替换它们。

我已经在 SO 上搜索了解决方案,但它们似乎都基于以下内容:

path = string.Join("_", path.Split(Path.GetInvalidFileNameChars()));

或类似的解决方案。然而,这些解决方案并不好,因为它们搞砸了驱动器号,我得到了以下输出:

C_\path\something\output_at_13_26_43.txt

我尝试使用Path.GetInvalidPathChars(),但它仍然不起作用,因为它没有在非法字符中包含:,所以它不会替换文件名中的那些。

所以,在弄清楚这一点后,我尝试这样做:

string dir = Path.GetDirectoryName(path);
string file = Path.GetFileName(path);
file = string.Join(replacement, file.Split(Path.GetInvalidFileNameChars()));
dir = string.Join(replacement, dir.Split(Path.GetInvalidPathChars()));

path = Path.Combine(dir, file);

但这也不好,因为文件名中的:s 似乎干扰了Path.GetFilename() 逻辑,它只返回最后一个: 之后的最后一个,所以我丢失了路径。

如何在没有 hacky 解决方案的情况下“正确”执行此操作?

【问题讨论】:

  • 驱动器号总是 2 个字符长,所以你应该做类似string driveLetter = path.Substring(0, 2); path = path.Substring(2, path.Length-2); 这样你在driveLetter 和“\path\something\output_at_13:26”中有“C:” :43.txt" 在path。在path 中应用替换并执行Path.Combine(driveLetter, path);
  • 你打败了我@ikerbera。不过,您可以在第二个子字符串调用中省略第二个参数。
  • @ikerbera - “驱动器号总是 2 个字符长”,但前提是它存在,它不一定存在(例如相对路径、UNC 路径)。

标签: c# filenames filepath


【解决方案1】:

您可以编写一个简单的清理程序来迭代每个字符并知道何时将冒号作为驱动器分隔符。这将捕获字母 A-Z 后跟“:”的任意组合。它还将检测路径分隔符而不是逃避它们。它不会检测输入字符串开头的空格,因此如果您的输入数据可能附带它们,您必须先对其进行修剪或相应地修改清理程序:

enum ParserState {
    PossibleDriveLetter,
    PossibleDriveLetterSeparator,
    Path
}

static string SanitizeFileName(string input) {
    StringBuilder output = new StringBuilder(input.Length);
    ParserState state = ParserState.PossibleDriveLetter;
    foreach(char current in input) {
        if (((current >= 'a') && (current <= 'z')) || ((current >= 'A') && (current <= 'Z'))) {
            output.Append(current);
            if (state == ParserState.PossibleDriveLetter) {
                state = ParserState.PossibleDriveLetterSeparator;
            }
            else {
                state = ParserState.Path;
            }
        }
        else if ((current == Path.DirectorySeparatorChar) ||
            (current == Path.AltDirectorySeparatorChar) ||
            ((current == ':') && (state == ParserState.PossibleDriveLetterSeparator)) ||
            !Path.GetInvalidFileNameChars().Contains(current)) {

            output.Append(current);
            state = ParserState.Path;
        }
        else {
            output.Append('_');
            state = ParserState.Path;
        }
    }
    return output.ToString();
}

你可以try it out here

【讨论】:

  • 我认为@Master_T 正在考虑一些已经存在于 .Net 环境中的方法,而不是必须创建自己的函数。不过,这是一个非常好的解决方案。
  • @ikerbera:我不知道有任何内置方法可以做到这一点。我不认为这个解决方案“hacky”,所以我认为它在问题的范围内。
  • +1 用于有趣的解决方案,虽然对于我在这里需要的东西有点矫枉过正,所以我选择了 CodeCaster 的解决方案。谢谢。
【解决方案2】:

您绝对应该确保您只收到有效的文件名。

如果你不能,并且你确定你的目录名称会是,你可以将路径拆分为最后一个反斜杠(假设是 Windows)并重新组合字符串:

public static string SanitizePath(string path)
{
    var lastBackslash = path.LastIndexOf('\\');

    var dir = path.Substring(0, lastBackslash);
    var file = path.Substring(lastBackslash, path.Length - lastBackslash);

    foreach (var invalid in Path.GetInvalidFileNameChars())
    {
        file = file.Replace(invalid, '_');
    }

    return dir + file;
}

【讨论】:

  • “在最后一个反斜杠上拆分”或斜杠:理想情况下使用 Path.DirectorySeparatorCharPath.AltDirectorySeparatorChar 以准备好独立于操作系统的 .NET Core。
  • 我最终采用了这个解决方案,在我的情况下简单但有效。
  • 为什么要查找最后一个反斜杠而不是使用Path.GetFileName
  • @Sefe 因为 OP 声称 "因为文件名中的 :s 似乎干扰了 Path.GetFilename() 逻辑,并且它只返回最后一个 :".
猜你喜欢
  • 1970-01-01
  • 2019-07-16
  • 2014-02-20
  • 2012-09-23
  • 2016-09-16
  • 1970-01-01
  • 2012-05-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多