题
我需要一种强大而简单的方法来从简单的字符串中删除非法路径和文件字符。我使用了下面的代码,但它似乎没有做任何事情,我错过了什么?
using System;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
string illegal = "\"M<>\"\\a/ry/ h**ad:>> a\\/:*?\"<>| li*tt|le|| la\"mb.?";
illegal = illegal.Trim(Path.GetInvalidFileNameChars());
illegal = illegal.Trim(Path.GetInvalidPathChars());
Console.WriteLine(illegal);
Console.ReadLine();
}
}
}
解决方案
尝试这样的事情;
string illegal = "\"M\"\\a/ry/ h**ad:>> a\\/:*?\"| li*tt|le|| la\"mb.?";
string invalid = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
foreach (char c in invalid)
{
illegal = illegal.Replace(c.ToString(), "");
}
但我必须同意这些评论,我可能会尝试处理非法路径的来源,而不是试图将非法路径变成合法但可能是非预期路径。
编辑:或使用Regex的潜在“更好”的解决方案。
string illegal = "\"M\"\\a/ry/ h**ad:>> a\\/:*?\"| li*tt|le|| la\"mb.?";
string regexSearch = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
Regex r = new Regex(string.Format("[{0}]", Regex.Escape(regexSearch)));
illegal = r.Replace(illegal, "");
然而,问题还有待提出,为什么你首先要这样做。
其他提示
public string GetSafeFilename(string filename)
{
return string.Join("_", filename.Split(Path.GetInvalidFileNameChars()));
}
这个答案是由Ceres提供的另一个主题,我真的很喜欢它简洁明了。
我使用Linq来清理文件名。您可以轻松扩展它以检查有效路径。
private static string CleanFileName(string fileName)
{
return Path.GetInvalidFileNameChars().Aggregate(fileName, (current, c) => current.Replace(c.ToString(), string.Empty));
}
更新
有些评论表明这种方法对他们不起作用,所以我已经包含了一个指向DotNetFiddle代码段的链接,因此您可以验证该方法。
您可以使用Linq删除非法字符,如下所示:
var invalidChars = Path.GetInvalidFileNameChars();
var invalidCharsRemoved = stringWithInvalidChars
.Where(x => !invalidChars.Contains(x))
.ToArray();
修改强>结果 这是它在评论中提到的所需编辑的外观:
var invalidChars = Path.GetInvalidFileNameChars();
string invalidCharsRemoved = new string(stringWithInvalidChars
.Where(x => !invalidChars.Contains(x))
.ToArray());
这些都是很好的解决方案,但它们都依赖于Path.GetInvalidFileNameChars
,这可能不如您想象的那么可靠。请注意有关 Path.GetInvalidPathChars
的MSDN文档中的以下注释:
从此方法返回的数组不保证包含文件和目录名称中无效的完整字符集。完整的无效字符集可能因文件系统而异。例如,在基于Windows的桌面平台上,无效路径字符可能包括ASCII / Unicode字符1到31,以及引用(<!> quot;),小于(<!> lt;),大于(<! > gt;),pipe(|),退格(\ b),null(\ 0)和制表符(\ t)。
使用 <=> 方法并不是更好。它包含完全相同的注释。
对于文件名:
string cleanFileName = String.Join("", fileName.Split(Path.GetInvalidFileNameChars()));
完整路径:
string cleanPath = String.Join("", path.Split(Path.GetInvalidPathChars()));
请注意,如果您打算将其用作安全功能,则更强大的方法是扩展所有路径,然后验证用户提供的路径确实是用户应该有权访问的目录的子级。
对于初学者,修剪只从头开始删除字符或字符串的结尾。其次,您应该评估是否确实要删除令人反感的字符,或者快速失败并让用户知道他们的文件名无效。我的选择是后者,但我的回答至少应该告诉你如何以正确和错误的方式做事:
StackOverflow问题显示如何检查给定字符串是否为有效文件名。请注意,您可以使用此问题的正则表达式删除带有正则表达式替换的字符(如果您确实需要这样做)。
我使用正则表达式来实现这一目标。首先,我动态构建正则表达式。
string regex = string.Format(
"[{0}]",
Regex.Escape(new string(Path.GetInvalidFileNameChars())));
Regex removeInvalidChars = new Regex(regex, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.CultureInvariant);
然后我只需调用removeInvalidChars.Replace来执行查找和替换。这显然可以扩展到覆盖路径字符。
从用户输入中删除非法字符的最佳方法是使用Regex类替换非法字符,在代码后面创建方法,或者使用RegularExpression控件在客户端验证。
public string RemoveSpecialCharacters(string str)
{
return Regex.Replace(str, "[^a-zA-Z0-9_]+", "_", RegexOptions.Compiled);
}
或强>
<asp:RegularExpressionValidator ID="regxFolderName"
runat="server"
ErrorMessage="Enter folder name with a-z A-Z0-9_"
ControlToValidate="txtFolderName"
Display="Dynamic"
ValidationExpression="^[a-zA-Z0-9_]*$"
ForeColor="Red">
我绝对更喜欢杰夫耶茨的想法。如果你稍微修改它,它将完美地工作:
string regex = String.Format("[{0}]", Regex.Escape(new string(Path.GetInvalidFileNameChars())));
Regex removeInvalidChars = new Regex(regex, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.CultureInvariant);
改进只是为了逃避自动生成的正则表达式。
这是一个应该有助于.NET 3及更高版本的代码片段。
using System.IO;
using System.Text.RegularExpressions;
public static class PathValidation
{
private static string pathValidatorExpression = "^[^" + string.Join("", Array.ConvertAll(Path.GetInvalidPathChars(), x => Regex.Escape(x.ToString()))) + "]+$";
private static Regex pathValidator = new Regex(pathValidatorExpression, RegexOptions.Compiled);
private static string fileNameValidatorExpression = "^[^" + string.Join("", Array.ConvertAll(Path.GetInvalidFileNameChars(), x => Regex.Escape(x.ToString()))) + "]+$";
private static Regex fileNameValidator = new Regex(fileNameValidatorExpression, RegexOptions.Compiled);
private static string pathCleanerExpression = "[" + string.Join("", Array.ConvertAll(Path.GetInvalidPathChars(), x => Regex.Escape(x.ToString()))) + "]";
private static Regex pathCleaner = new Regex(pathCleanerExpression, RegexOptions.Compiled);
private static string fileNameCleanerExpression = "[" + string.Join("", Array.ConvertAll(Path.GetInvalidFileNameChars(), x => Regex.Escape(x.ToString()))) + "]";
private static Regex fileNameCleaner = new Regex(fileNameCleanerExpression, RegexOptions.Compiled);
public static bool ValidatePath(string path)
{
return pathValidator.IsMatch(path);
}
public static bool ValidateFileName(string fileName)
{
return fileNameValidator.IsMatch(fileName);
}
public static string CleanPath(string path)
{
return pathCleaner.Replace(path, "");
}
public static string CleanFileName(string fileName)
{
return fileNameCleaner.Replace(fileName, "");
}
}
上面的大多数解决方案都将路径和文件名的非法字符组合在一起是错误的(即使两个调用当前都返回相同的字符集)。我首先在路径和文件名中拆分路径+文件名,然后将相应的设置应用于它们,然后再将它们组合起来。
wvd_vegt
如果使用单个字符删除或替换无效字符,则可能会发生冲突:
<abc -> abc
>abc -> abc
这是避免这种情况的简单方法:
public static string ReplaceInvalidFileNameChars(string s)
{
char[] invalidFileNameChars = System.IO.Path.GetInvalidFileNameChars();
foreach (char c in invalidFileNameChars)
s = s.Replace(c.ToString(), "[" + Array.IndexOf(invalidFileNameChars, c) + "]");
return s;
}
结果:
<abc -> [1]abc
>abc -> [2]abc
抛出异常。
if ( fileName.IndexOfAny(Path.GetInvalidFileNameChars()) > -1 )
{
throw new ArgumentException();
}
我为了好玩而写了这个怪物,它可以让你往返:
public static class FileUtility
{
private const char PrefixChar = '%';
private static readonly int MaxLength;
private static readonly Dictionary<char,char[]> Illegals;
static FileUtility()
{
List<char> illegal = new List<char> { PrefixChar };
illegal.AddRange(Path.GetInvalidFileNameChars());
MaxLength = illegal.Select(x => ((int)x).ToString().Length).Max();
Illegals = illegal.ToDictionary(x => x, x => ((int)x).ToString("D" + MaxLength).ToCharArray());
}
public static string FilenameEncode(string s)
{
var builder = new StringBuilder();
char[] replacement;
using (var reader = new StringReader(s))
{
while (true)
{
int read = reader.Read();
if (read == -1)
break;
char c = (char)read;
if(Illegals.TryGetValue(c,out replacement))
{
builder.Append(PrefixChar);
builder.Append(replacement);
}
else
{
builder.Append(c);
}
}
}
return builder.ToString();
}
public static string FilenameDecode(string s)
{
var builder = new StringBuilder();
char[] buffer = new char[MaxLength];
using (var reader = new StringReader(s))
{
while (true)
{
int read = reader.Read();
if (read == -1)
break;
char c = (char)read;
if (c == PrefixChar)
{
reader.Read(buffer, 0, MaxLength);
var encoded =(char) ParseCharArray(buffer);
builder.Append(encoded);
}
else
{
builder.Append(c);
}
}
}
return builder.ToString();
}
public static int ParseCharArray(char[] buffer)
{
int result = 0;
foreach (char t in buffer)
{
int digit = t - '0';
if ((digit < 0) || (digit > 9))
{
throw new ArgumentException("Input string was not in the correct format");
}
result *= 10;
result += digit;
}
return result;
}
}
我认为使用正则表达式进行验证并指定允许哪些字符更容易,而不是尝试检查所有不良字符。 看到这些链接: http://www.c-sharpcorner.com/UploadFile/prasad_1/ RegExpPSD12062005021717AM / RegExpPSD.aspx http://www.windowsdevcenter.com/pub/a/ oreilly和/窗/消息/ csharp_0101.html
另外,搜索<!> quot;正则表达式编辑器<!>,s,它们有很多帮助。有些甚至可以为你输出c#中的代码。
这似乎是O(n)并且不会在字符串上花费太多内存:
private static readonly HashSet<char> invalidFileNameChars = new HashSet<char>(Path.GetInvalidFileNameChars());
public static string RemoveInvalidFileNameChars(string name)
{
if (!name.Any(c => invalidFileNameChars.Contains(c))) {
return name;
}
return new string(name.Where(c => !invalidFileNameChars.Contains(c)).ToArray());
}
扫描这里的答案,它们似乎都涉及使用无效文件名字符的字符数组。
当然,这可能是微优化 - 但是为了任何可能想要检查大量值作为有效文件名的人的利益,值得注意的是,构建无效字符的哈希集将带来显着更好的性能。
过去我非常惊讶(震惊)一个哈希集(或词典)在列表上的迭代速度有多快。对于字符串,它是一个非常低的数字(从内存中约5-7项)。对于大多数其他简单数据(对象引用,数字等),魔术交叉似乎大约有20个项目。
Path.InvalidFileNameChars <!>“list <!>”中有40个无效字符。今天进行了搜索,并且在StackOverflow上有一个非常好的基准测试,显示散列集将占用40个项目的数组/列表的一半多一点时间:https://stackoverflow.com/a/10762995/949129
这是我用于清理路径的辅助类。我现在忘记为什么我有一个花哨的替换选项,但它是一个可爱的奖金。
额外奖励方法<!>“IsValidLocalPath <!>”;也是:)
(**不使用正则表达式的那些)
public static class PathExtensions
{
private static HashSet<char> _invalidFilenameChars;
private static HashSet<char> InvalidFilenameChars
{
get { return _invalidFilenameChars ?? (_invalidFilenameChars = new HashSet<char>(Path.GetInvalidFileNameChars())); }
}
/// <summary>Replaces characters in <c>text</c> that are not allowed in file names with the
/// specified replacement character.</summary>
/// <param name="text">Text to make into a valid filename. The same string is returned if
/// it is valid already.</param>
/// <param name="replacement">Replacement character, or NULL to remove bad characters.</param>
/// <param name="fancyReplacements">TRUE to replace quotes and slashes with the non-ASCII characters ” and ⁄.</param>
/// <returns>A string that can be used as a filename. If the output string would otherwise be empty, "_" is returned.</returns>
public static string ToValidFilename(this string text, char? replacement = '_', bool fancyReplacements = false)
{
StringBuilder sb = new StringBuilder(text.Length);
HashSet<char> invalids = InvalidFilenameChars;
bool changed = false;
for (int i = 0; i < text.Length; i++)
{
char c = text[i];
if (invalids.Contains(c))
{
changed = true;
char repl = replacement ?? '\0';
if (fancyReplacements)
{
if (c == '"') repl = '”'; // U+201D right double quotation mark
else if (c == '\'') repl = '’'; // U+2019 right single quotation mark
else if (c == '/') repl = '⁄'; // U+2044 fraction slash
}
if (repl != '\0')
sb.Append(repl);
}
else
sb.Append(c);
}
if (sb.Length == 0)
return "_";
return changed ? sb.ToString() : text;
}
/// <summary>
/// Returns TRUE if the specified path is a valid, local filesystem path.
/// </summary>
/// <param name="pathString"></param>
/// <returns></returns>
public static bool IsValidLocalPath(this string pathString)
{
// From solution at https://stackoverflow.com/a/11636052/949129
Uri pathUri;
Boolean isValidUri = Uri.TryCreate(pathString, UriKind.Absolute, out pathUri);
return isValidUri && pathUri != null && pathUri.IsLoopback;
}
}
public static class StringExtensions
{
public static string RemoveUnnecessary(this string source)
{
string result = string.Empty;
string regex = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
Regex reg = new Regex(string.Format("[{0}]", Regex.Escape(regex)));
result = reg.Replace(source, "");
return result;
}
}
您可以清楚地使用方法。
文件名不能包含Path.GetInvalidPathChars()
,+
和#
符号以及其他特定名称中的字符。我们将所有检查合并为一个类:
public static class FileNameExtensions
{
private static readonly Lazy<string[]> InvalidFileNameChars =
new Lazy<string[]>(() => Path.GetInvalidPathChars()
.Union(Path.GetInvalidFileNameChars()
.Union(new[] { '+', '#' })).Select(c => c.ToString(CultureInfo.InvariantCulture)).ToArray());
private static readonly HashSet<string> ProhibitedNames = new HashSet<string>
{
@"aux",
@"con",
@"clock$",
@"nul",
@"prn",
@"com1",
@"com2",
@"com3",
@"com4",
@"com5",
@"com6",
@"com7",
@"com8",
@"com9",
@"lpt1",
@"lpt2",
@"lpt3",
@"lpt4",
@"lpt5",
@"lpt6",
@"lpt7",
@"lpt8",
@"lpt9"
};
public static bool IsValidFileName(string fileName)
{
return !string.IsNullOrWhiteSpace(fileName)
&& fileName.All(o => !IsInvalidFileNameChar(o))
&& !IsProhibitedName(fileName);
}
public static bool IsProhibitedName(string fileName)
{
return ProhibitedNames.Contains(fileName.ToLower(CultureInfo.InvariantCulture));
}
private static string ReplaceInvalidFileNameSymbols([CanBeNull] this string value, string replacementValue)
{
if (value == null)
{
return null;
}
return InvalidFileNameChars.Value.Aggregate(new StringBuilder(value),
(sb, currentChar) => sb.Replace(currentChar, replacementValue)).ToString();
}
public static bool IsInvalidFileNameChar(char value)
{
return InvalidFileNameChars.Value.Contains(value.ToString(CultureInfo.InvariantCulture));
}
public static string GetValidFileName([NotNull] this string value)
{
return GetValidFileName(value, @"_");
}
public static string GetValidFileName([NotNull] this string value, string replacementValue)
{
if (string.IsNullOrWhiteSpace(value))
{
throw new ArgumentException(@"value should be non empty", nameof(value));
}
if (IsProhibitedName(value))
{
return (string.IsNullOrWhiteSpace(replacementValue) ? @"_" : replacementValue) + value;
}
return ReplaceInvalidFileNameSymbols(value, replacementValue);
}
public static string GetFileNameError(string fileName)
{
if (string.IsNullOrWhiteSpace(fileName))
{
return CommonResources.SelectReportNameError;
}
if (IsProhibitedName(fileName))
{
return CommonResources.FileNameIsProhibited;
}
var invalidChars = fileName.Where(IsInvalidFileNameChar).Distinct().ToArray();
if(invalidChars.Length > 0)
{
return string.Format(CultureInfo.CurrentCulture,
invalidChars.Length == 1 ? CommonResources.InvalidCharacter : CommonResources.InvalidCharacters,
StringExtensions.JoinQuoted(@",", @"'", invalidChars.Select(c => c.ToString(CultureInfo.CurrentCulture))));
}
return string.Empty;
}
}
方法GetValidFileName
将所有不正确的数据替换为_
。
用于清除Windows文件命名的任何非法字符的字符串:
public static string CleanIllegalName(string p_testName) => new Regex(string.Format("[{0}]", Regex.Escape(new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars())))).Replace(p_testName, "");
public static bool IsValidFilename(string testName)
{
return !new Regex("[" + Regex.Escape(new String(System.IO.Path.GetInvalidFileNameChars())) + "]").IsMatch(testName);
}
这确实是你想要的,并避免碰撞
static string SanitiseFilename(string key)
{
var invalidChars = Path.GetInvalidFileNameChars();
var sb = new StringBuilder();
foreach (var c in key)
{
var invalidCharIndex = -1;
for (var i = 0; i < invalidChars.Length; i++)
{
if (c == invalidChars[i])
{
invalidCharIndex = i;
}
}
if (invalidCharIndex > -1)
{
sb.Append("_").Append(invalidCharIndex);
continue;
}
if (c == '_')
{
sb.Append("__");
continue;
}
sb.Append(c);
}
return sb.ToString();
}
我认为问题已经没有完全回答...... 答案只描述干净的文件名或路径......不是两者兼而有之。这是我的解决方案:
private static string CleanPath(string path)
{
string regexSearch = new string(Path.GetInvalidFileNameChars()) + new string(Path.GetInvalidPathChars());
Regex r = new Regex(string.Format("[{0}]", Regex.Escape(regexSearch)));
List<string> split = path.Split('\\').ToList();
string returnValue = split.Aggregate(string.Empty, (current, s) => current + (r.Replace(s, "") + @"\"));
returnValue = returnValue.TrimEnd('\\');
return returnValue;
}
我创建了一个结合了几个建议的扩展方法:
- 在哈希集中保留非法字符
- 过滤掉ascii 127下面的字符。由于Path.GetInvalidFileNameChars不包含所有使用ascii代码从0到255的无效字符。见这里和 cs-lang = vb#Anchor_1“rel =”nofollow noreferrer“> MSDN
- 定义替换字符的可能性 醇>
来源:
public static class FileNameCorrector
{
private static HashSet<char> invalid = new HashSet<char>(Path.GetInvalidFileNameChars());
public static string ToValidFileName(this string name, char replacement = '\0')
{
var builder = new StringBuilder();
foreach (var cur in name)
{
if (cur > 31 && cur < 128 && !invalid.Contains(cur))
{
builder.Append(cur);
}
else if (replacement != '\0')
{
builder.Append(replacement);
}
}
return builder.ToString();
}
}
或者你可以做到
[YOUR STRING].Replace('\\', ' ').Replace('/', ' ').Replace('"', ' ').Replace('*', ' ').Replace(':', ' ').Replace('?', ' ').Replace('<', ' ').Replace('>', ' ').Replace('|', ' ').Trim();