有没有办法在 c# 中使字符串文件路径安全?

问题描述 投票:0回答:15

我的程序将从互联网上获取任意字符串并将它们用作文件名。有没有一种简单的方法可以从这些字符串中删除坏字符,或者我是否需要为此编写一个自定义函数?

c# .net string filepath
15个回答
201
投票

呃,当人们试图猜测哪些字符有效时,我讨厌它。除了完全不可移植(总是考虑 Mono)之外,前面的两条评论都遗漏了 25 个无效字符。

foreach (var c in Path.GetInvalidFileNameChars()) 
{ 
  fileName = fileName.Replace(c, '-'); 
}

或者在 VB 中:

'Clean just a filename
Dim filename As String = "salmnas dlajhdla kjha;dmas'lkasn"
For Each c In IO.Path.GetInvalidFileNameChars
    filename = filename.Replace(c, "")
Next

'See also IO.Path.GetInvalidPathChars

45
投票

去除无效字符:

static readonly char[] invalidFileNameChars = Path.GetInvalidFileNameChars();

// Builds a string out of valid chars
var validFilename = new string(filename.Where(ch => !invalidFileNameChars.Contains(ch)).ToArray());

替换无效字符:

static readonly char[] invalidFileNameChars = Path.GetInvalidFileNameChars();

// Builds a string out of valid chars and an _ for invalid ones
var validFilename = new string(filename.Select(ch => invalidFileNameChars.Contains(ch) ? '_' : ch).ToArray());

替换无效字符(并避免潜在的名称冲突,如 Hell* 与 Hell$):

static readonly IList<char> invalidFileNameChars = Path.GetInvalidFileNameChars();

// Builds a string out of valid chars and replaces invalid chars with a unique letter (Moves the Char into the letter range of unicode, starting at "A")
var validFilename = new string(filename.Select(ch => invalidFileNameChars.Contains(ch) ? Convert.ToChar(invalidFileNameChars.IndexOf(ch) + 65) : ch).ToArray());

34
投票

这个问题已经被问过manytimesbefore而且,正如之前多次指出的那样,

IO.Path.GetInvalidFileNameChars
是不够的。

首先,有很多名称如 PRN 和 CON 是保留的,不允许作为文件名。仅在根文件夹中不允许使用其他名称。也不允许使用以句点结尾的名称。

第二,有各种长度限制。阅读 NTFS 的完整列表here.

第三,您可以附加到具有其他限制的文件系统。例如,ISO 9660 文件名不能以“-”开头,但可以包含它。

四、如果两个进程“任意”取同一个名字怎么办?

一般来说,使用外部生成的名称作为文件名是个坏主意。我建议生成您自己的私有文件名并在内部存储人类可读的名称。


22
投票

我同意 Grauenwolf 的观点并强烈推荐

Path.GetInvalidFileNameChars()

这是我的 C# 贡献:

string file = @"38?/.\}[+=n a882 a.a*/|n^%$ ad#(-))";
Array.ForEach(Path.GetInvalidFileNameChars(), 
      c => file = file.Replace(c.ToString(), String.Empty));

附注- 这比它应该的更神秘 - 我试图简明扼要。


14
投票

这是我的版本:

static string GetSafeFileName(string name, char replace = '_') {
  char[] invalids = Path.GetInvalidFileNameChars();
  return new string(name.Select(c => invalids.Contains(c) ? replace : c).ToArray());
}

我不确定 GetInvalidFileNameChars 的结果是如何计算出来的,但是“Get”表明它不是平凡的,所以我缓存了结果。此外,这只遍历输入字符串一次而不是多次,就像上面迭代无效字符集的解决方案一样,一次一个地替换源字符串中的它们。此外,我喜欢基于 Where 的解决方案,但我更喜欢替换无效字符而不是删除它们。最后,我的替换正好是一个字符,以避免在遍历字符串时将字符转换为字符串。

我说的都是没有做分析的——这个对我来说只是“感觉”很好。 :)


13
投票

这是我现在使用的函数(感谢 jcollum 提供的 C# 示例):

public static string MakeSafeFilename(string filename, char replaceChar)
{
    foreach (char c in System.IO.Path.GetInvalidFileNameChars())
    {
        filename = filename.Replace(c, replaceChar);
    }
    return filename;
}

为了方便起见,我只是把它放在“Helpers”类中。


7
投票

如果你想快速删除所有特殊字符,这对于文件名来说有时更容易被用户阅读,这很有效:

string myCrazyName = "q`w^e!r@t#y$u%i^o&p*a(s)d_f-g+h=j{k}l|z:x\"c<v>b?n[m]q\\w;e'r,t.y/u";
string safeName = Regex.Replace(
    myCrazyName,
    "\W",  /*Matches any nonword character. Equivalent to '[^A-Za-z0-9_]'*/
    "",
    RegexOptions.IgnoreCase);
// safeName == "qwertyuiopasd_fghjklzxcvbnmqwertyu"

6
投票

这是我刚刚添加到 ClipFlair 的 (http://github.com/Zoomicon/ClipFlair) StringExtensions 静态类(Utils.Silverlight 项目)的内容,基于从上面 Dour High Arch 发布的相关 stackoverflow 问题的链接收集的信息:

public static string ReplaceInvalidFileNameChars(this string s, string replacement = "")
{
  return Regex.Replace(s,
    "[" + Regex.Escape(new String(System.IO.Path.GetInvalidPathChars())) + "]",
    replacement, //can even use a replacement string of any length
    RegexOptions.IgnoreCase);
    //not using System.IO.Path.InvalidPathChars (deprecated insecure API)
}

5
投票
static class Utils
{
    public static string MakeFileSystemSafe(this string s)
    {
        return new string(s.Where(IsFileSystemSafe).ToArray());
    }

    public static bool IsFileSystemSafe(char c)
    {
        return !Path.GetInvalidFileNameChars().Contains(c);
    }
}

5
投票

为什么不像这样将字符串转换为 Base64 等价物:

string UnsafeFileName = "salmnas dlajhdla kjha;dmas'lkasn";
string SafeFileName = Convert.ToBase64String(Encoding.UTF8.GetBytes(UnsafeFileName));

如果你想把它转换回来以便你可以阅读它:

UnsafeFileName = Encoding.UTF8.GetString(Convert.FromBase64String(SafeFileName));

我用它来保存具有随机描述的唯一名称的 PNG 文件。


2
投票
private void textBoxFileName_KeyPress(object sender, KeyPressEventArgs e)
{
   e.Handled = CheckFileNameSafeCharacters(e);
}

/// <summary>
/// This is a good function for making sure that a user who is naming a file uses proper characters
/// </summary>
/// <param name="e"></param>
/// <returns></returns>
internal static bool CheckFileNameSafeCharacters(System.Windows.Forms.KeyPressEventArgs e)
{
    if (e.KeyChar.Equals(24) || 
        e.KeyChar.Equals(3) || 
        e.KeyChar.Equals(22) || 
        e.KeyChar.Equals(26) || 
        e.KeyChar.Equals(25))//Control-X, C, V, Z and Y
            return false;
    if (e.KeyChar.Equals('\b'))//backspace
        return false;

    char[] charArray = Path.GetInvalidFileNameChars();
    if (charArray.Contains(e.KeyChar))
       return true;//Stop the character from being entered into the control since it is non-numerical
    else
        return false;            
}

2
投票

从我以前的项目中,我找到了这个解决方案,它已经运行了 2 年多。我用“!”替换非法字符,然后检查双!!,使用你自己的字符。

    public string GetSafeFilename(string filename)
    {
        string res = string.Join("!", filename.Split(Path.GetInvalidFileNameChars()));

        while (res.IndexOf("!!") >= 0)
            res = res.Replace("!!", "!");

        return res;
    }

1
投票

我发现使用它既快速又容易理解:

<Extension()>
Public Function MakeSafeFileName(FileName As String) As String
    Return FileName.Where(Function(x) Not IO.Path.GetInvalidFileNameChars.Contains(x)).ToArray
End Function

这是有效的,因为

string
IEnumerable
作为
char
数组,并且有一个
string
构造函数字符串采用
char
数组。


1
投票

许多人建议使用

Path.GetInvalidFileNameChars()
,这对我来说似乎是一个糟糕的解决方案。我鼓励你使用白名单而不是黑名单,因为黑客最终总会找到绕过它的方法。

这是您可以使用的代码示例:

    string whitelist = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.";
    foreach (char c in filename)
    {
        if (!whitelist.Contains(c))
        {
            filename = filename.Replace(c, '-');
        }
    }

0
投票

我采用了 Jonathan Allen 的回答 并制作了一个可以在任何字符串上调用的扩展方法。

public static class StringExtensions
{
    public static string ReplaceInvalidFileNameChars(this string input, char replaceCharacter = '-')
    {
        foreach (char c in Path.GetInvalidFileNameChars())
        {
            input = input.Replace(c, replaceCharacter);
        }

        return input;
    }
}

这可以像这样使用:

string myFileName = "test > file ? name.txt";

string myValidFileName1 = myFileName.ReplaceInvalidFileNameChars();
string myValidFileName2 = myFileName.ReplaceInvalidFileNameChars('');
string myValidFileName3 = myFileName.ReplaceInvalidFileNameChars('_');
© www.soinside.com 2019 - 2024. All rights reserved.