我的程序将从互联网上获取任意字符串并将它们用作文件名。有没有一种简单的方法可以从这些字符串中删除坏字符,还是我需要为此编写一个自定义函数?
14 回答
呃,当人们试图猜测哪些字符是有效的时,我讨厌它。除了完全不可移植(一直在考虑 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
要去除无效字符:
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());
这个问题之前已经被问过很多 次了,正如 之前多次指出的那样,这个问题IO.Path.GetInvalidFileNameChars
是不充分的。
首先,有许多名称(例如 PRN 和 CON)是保留的并且不允许用于文件名。还有其他名称不允许仅在根文件夹中。也不允许使用以句点结尾的名称。
其次,有多种长度限制。在此处阅读 NTFS 的完整列表。
第三,您可以附加到具有其他限制的文件系统。例如,ISO 9660 文件名不能以“-”开头,但可以包含它。
四、如果两个进程“任意”取同名怎么办?
一般来说,使用外部生成的文件名是一个坏主意。我建议生成您自己的私有文件名并在内部存储人类可读的名称。
我同意 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));
ps——这比它应该的更神秘——我试图简洁。
这是我的版本:
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 的解决方案,但我更喜欢替换无效字符而不是删除它们。最后,我的替换正好是一个字符,以避免在迭代字符串时将字符转换为字符串。
我说了所有没有做剖析的事情——这个对我来说只是“感觉”很好。:)
这是我现在使用的函数(感谢 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;
}
为了方便起见,我只是把它放在“帮助者”类中。
如果您想快速去除所有特殊字符,这些特殊字符有时对于文件名来说更易于用户阅读,这很好用:
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"
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);
}
}
这是我刚刚添加到 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)
}
为什么不将字符串转换为 Base64 等效项,如下所示:
string UnsafeFileName = "salmnas dlajhdla kjha;dmas'lkasn";
string SafeFileName = Convert.ToBase64String(Encoding.UTF8.GetBytes(UnsafeFileName));
如果您想将其转换回来以便阅读:
UnsafeFileName = Encoding.UTF8.GetString(Convert.FromBase64String(SafeFileName));
我用它来保存随机描述中具有唯一名称的 PNG 文件。
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 年。我用“!”替换非法字符,然后检查双 !!,使用你自己的字符。
public string GetSafeFilename(string filename)
{
string res = string.Join("!", filename.Split(Path.GetInvalidFileNameChars()));
while (res.IndexOf("!!") >= 0)
res = res.Replace("!!", "!");
return res;
}
我发现使用它既快速又容易理解:
<Extension()>
Public Function MakeSafeFileName(FileName As String) As String
Return FileName.Where(Function(x) Not IO.Path.GetInvalidFileNameChars.Contains(x)).ToArray
End Function
这是有效的,因为 astring
是IEnumerable
一个char
数组,并且有一个带有数组的string
构造函数字符串char
。
许多人建议使用Path.GetInvalidFileNameChars()
这对我来说似乎是一个糟糕的解决方案。我鼓励您使用白名单而不是黑名单,因为黑客总能找到最终绕过它的方法。
这是您可以使用的代码示例:
string whitelist = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.";
foreach (char c in filename)
{
if (!whitelist.Contains(c))
{
filename = filename.Replace(c, '-');
}
}