如何从路径和文件名中删除非法字符?

Gar*_*hby 423 c# string directory path

我需要一种强大而简单的方法来从简单的字符串中删除非法路径和文件字符.我使用了下面的代码,但它似乎没有做任何事情,我错过了什么?

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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Mat*_*ley 473

尝试这样的事情;

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(), ""); 
}
Run Code Online (Sandbox Code Playgroud)

但我不得不同意这些评论,我可能会尝试处理非法路径的来源,而不是试图将非法路径变成合法但可能是非预期路径.

编辑:或使用正则表达式的潜在"更好"的解决方案.

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, "");
Run Code Online (Sandbox Code Playgroud)

不过,问题还有待提出,为什么你首先要这样做.

  • 没有必要将两个列表附加在一起.非法文件名字符列表包含非法路径字符列表,还有一些.以下是转换为int的两个列表的列表:34,60,62,124,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,58,42,63,92,47 34,60,62,124,0,1,2 ,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27 ,28,29,30,31 (36认同)
  • @Charleh这个讨论是如此不必要......代码应该始终优化,并且不存在不正确的风险.文件名也是路径的一部分.因此,`GetInvalidPathChars()`可能包含`GetInvalidFileNameChars()`不会出现的字符,这是不合逻辑的.你没有对"过早"的优化采取正确的态度.你只是使用不好的代码. (11认同)
  • @sjbotha这可能适用于Windows和Microsoft的.NET实现我不愿意为单声道运行Linux做同样的假设. (7认同)
  • 关于第一个解决方案.StringBuilder不应该比字符串赋值更有效吗? (7认同)
  • 值得称道的是@MatthewScharley,GetInvalidPathChars()的Mono实现仅返回0x00,而GetInvalidFileNameChars()在非Windows平台上运行时仅返回0x00和'/'.在Windows上,无效字符列表要长得多,并且GetInvalidPathChars()在GetInvalidFileNameChars()中完全重复.这在可预见的未来不会发生变化,所以你真正做的就是将这个函数运行的时间加倍,因为你担心有效路径的定义会很快改变.它不会. (5认同)
  • @Warren:如果你真的很担心的话,可以随意重复演绎得到的字符串,但我们在这里说得非常诚实:对于字符串的20到40次迭代与平均路径的长度之间的差异(假设100个字符是慷慨的)将会使与函数的运行时完全相同*没有*差异.对于所有*实用*目的,没有必要担心它.另一方面,这两个函数确实用于不同的目的(至少在我看来),对于某个给定的文件系统,一个函数不返回另一个函数的超集是完全合理的. (4认同)
  • 什么是消毒问题,Bob Tables? (3认同)
  • GetInvalidFileNameChars()总是 - 总是,你听到我 - 将包含在GetInvalidPathChars()中的所有内容,因为文件中的某个字符无法在路径名中无效.今天没有文件系统允许这样,没有文件系统.无论如何,微软自己的这些函数文档非常清楚地表明你不应该期望字符列表保证准确,因为文件系统可能支持不同的东西. (3认同)
  • 我可能会在这里与马修站在一起,只是说假设是所有混乱的母亲.您正在谈论优化代码,这些代码可能不需要针对潜在的正确性进行优化.我会在任何一天对过早优化采取正确的态度 (3认同)
  • @JoeyAdams:看到我对Sarel Botha的回复.简而言之,一个是Windows上另一个的超集.就个人而言,我不愿意在跨平台上做同样的赌注,而C#和.NET一般都是通过Mono获得更广泛的观众. (2认同)
  • 如何将工作加倍(无论是对数组进行重复数据删除,还是两次运行几乎完全相同的数组值)“完全没有区别”?你和我一样知道这是不正确的,所以-不要--说--它-。我们正在努力成为 Stackoverflow 的教育资源,而不是一个因被告知你错了而引起的修辞华丽的地方。让我们明确一点:您在这里推荐的内容实际上与旧的 Daily WTF 关于提供您自己的 TRUE 和 FALSE 定义相同,因为您不能相信编译器或库总是正确的。 (2认同)
  • 我个人更喜欢这种方式:`var invalid = Path.GetInvalidFileNameChars().Union(Path.GetInvalidPathChars()); foreach(char c in invalid) 非法 = 非法.Replace(c.ToString(), "_");` (2认同)
  • 我不知道你们为什么这么关心他为什么要使用它。有各种合法的场景,这将是有用的。例如,我们的应用程序将 xlsx 文件作为报告输出到电子邮件中,如果我们在输入时不对其进行验证,您将在计划创建报告的时间之前不知道文件名无效。我们遇到过问题,过去有人不小心在文件名中输入了一个小于号并保存了它。另外,我们的一些客户端运行 linux,一些运行 Windows,因此允许的文件不一样。 (2认同)

She*_*wzy 298

public string RemoveInvalidChars(string filename)
{
    return string.Concat(filename.Split(Path.GetInvalidFileNameChars()));
}
Run Code Online (Sandbox Code Playgroud)

这个答案是Ceres的另一个主题,我真的很喜欢它简洁明了.

  • 我测试了这个问题的五种方法(定时循环为100,000),这种方法是最快的.正则表达式排在第2位,比此方法慢25%. (30认同)
  • 要准确回答OP的问题,你需要使用""代替"_",但你的答案可能适用于我们在实践中的更多人.我认为用一些合法的字符替换非法字符更常见. (10认同)
  • 要解决@BH的注释,可以简单地使用string.Concat(name.Split(Path.GetInvalidFileNameChars())) (9认同)

Mic*_*ton 206

我使用Linq来清理文件名.您可以轻松扩展它以检查有效路径.

private static string CleanFileName(string fileName)
{
    return Path.GetInvalidFileNameChars().Aggregate(fileName, (current, c) => current.Replace(c.ToString(), string.Empty));
}
Run Code Online (Sandbox Code Playgroud)

更新

一些评论表明这种方法对他们不起作用所以我已经包含了一个指向DotNetFiddle代码段的链接,因此您可以验证该方法.

https://dotnetfiddle.net/nw1SWY

  • 这不适合我.该方法不返回干净的字符串.它按原样返回传递的文件名. (4认同)
  • 这很容易 - 人们正在传递有效字符串.赞成酷聚合解决方案. (3认同)
  • @Karan或Jon您发送此功能的输入是什么?请参阅我的编辑以验证此方法. (2认同)

Gre*_*vec 88

您可以使用Linq删除非法字符,如下所示:

var invalidChars = Path.GetInvalidFileNameChars();

var invalidCharsRemoved = stringWithInvalidChars
.Where(x => !invalidChars.Contains(x))
.ToArray();
Run Code Online (Sandbox Code Playgroud)

编辑
这是注释中提到的所需编辑的外观:

var invalidChars = Path.GetInvalidFileNameChars();

string invalidCharsRemoved = new string(stringWithInvalidChars
  .Where(x => !invalidChars.Contains(x))
  .ToArray());
Run Code Online (Sandbox Code Playgroud)

  • 我知道这是一个老问题,但这是一个很棒的答案.但是,我想在c#中添加你无法隐式或显式地从char []转换为字符串(疯狂,我知道)所以你需要将它放入字符串构造函数中. (5认同)
  • @anjdreas实际上Path.GetInvalidPathChars()似乎是Path.GetInvalidFileNameChars()的子集,而不是相反的方式.例如,Path.GetInvalidPathChars()不会返回'?'. (2认同)

Ren*_*ené 27

这些都是很好的解决方案,但它们都依赖于Path.GetInvalidFileNameChars,这可能不像你想象的那么可靠.请注意MSDN文档中的以下注释Path.GetInvalidFileNameChars:

从此方法返回的数组不保证包含在文件和目录名称中无效的完整字符集.完整的无效字符集可能因文件系统而异.例如,在基于Windows的桌面平台上,无效路径字符可能包括ASCII/Unicode字符1到31,以及quote("),小于(<),大于(>),pipe(|),退格(\b),null(\ 0)和制表符(\ t).

方法并没有更好Path.GetInvalidPathChars.它包含完全相同的注释.

  • 那么Path.GetInvalidFileNameChars有什么意义呢?我希望它能够准确地返回当前系统的无效字符,依靠.NET来了解我正在运行的文件系统并向我提供适合的无效字符.如果不是这种情况并且它只返回硬编码字符,这些字符首先不可靠,则应删除此方法,因为它的值为零. (12认同)
  • @ fantastik78好点,但在这种情况下,我想要一个额外的枚举参数来指定我的远程FS.如果这是太多的维护工作(最有可能的情况),这整个方法仍然是一个坏主意,因为它给你错误的安全印象. (3认同)

Lil*_*ley 23

对于文件名:

string cleanFileName = String.Join("", fileName.Split(Path.GetInvalidFileNameChars()));
Run Code Online (Sandbox Code Playgroud)

对于完整路径:

string cleanPath = String.Join("", path.Split(Path.GetInvalidPathChars()));
Run Code Online (Sandbox Code Playgroud)


use*_*116 18

对于初学者,Trim仅从字符串的开头或结尾删除字符.其次,您应该评估是否确实要删除令人反感的字符,或者快速失败并让用户知道他们的文件名无效.我的选择是后者,但我的回答至少应该告诉你如何以正确和错误的方式做事:

StackOverflow问题显示如何检查给定字符串是否是有效的文件名.请注意,您可以使用此问题的正则表达式删除带有正则表达式替换的字符(如果您确实需要这样做).

  • 我通常会同意第二个,但我有一个生成文件名的程序,在某些情况下可能包含非法字符.由于*我的程序*正在生成非法文件名,我认为删除/替换这些字符是合适的.(只是指出一个有效的用例) (4认同)

Jef*_*tes 16

我使用正则表达式来实现这一点.首先,我动态构建正则表达式.

string regex = string.Format(
                   "[{0}]",
                   Regex.Escape(new string(Path.GetInvalidFileNameChars())));
Regex removeInvalidChars = new Regex(regex, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.CultureInvariant);
Run Code Online (Sandbox Code Playgroud)

然后我只需调用removeInvalidChars.Replace来执行查找和替换.这显然可以扩展到覆盖路径字符.

  • 我还将添加一些可以在[MSDN](http://msdn.microsoft.com/zh-cn/library/aa365247.aspx#namespaces)上找到的其他无效文件名模式,并将您的解决方案扩展到以下正则表达式: `new Regex(String.Format(“ ^(CON | PRN | AUX | NUL | CLOCK \ $ | COM [1-9] | LPT [1-9])(?= \ .. | $)|(^( \。+ | \ s +)$)|((\。+ | \ s +)$)|([{{0}])“,Regex.Escape(new String(Path.GetInvalidFileNameChars()))),RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.CultureInvariant);` (2认同)

ano*_*ani 15

从用户输入中删除非法字符的最佳方法是使用Regex类替换非法字符,在代码后面创建方法,或者使用RegularExpression控件在客户端验证.

public string RemoveSpecialCharacters(string str)
{
    return Regex.Replace(str, "[^a-zA-Z0-9_]+", "_", RegexOptions.Compiled);
}
Run Code Online (Sandbox Code Playgroud)

要么

<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">
Run Code Online (Sandbox Code Playgroud)

  • 恕我直言这个解决方案比其他人好得多,而不是搜索所有无效的字符,只需定义哪些是有效的. (4认同)
  • 对于 [POSIX“完全可移植的文件名”](https://en.wikipedia.org/wiki/Filename),请使用 `"[^a-zA-Z0-9_.-]+"` (2认同)

Jan*_*Jan 14

我绝对更喜欢杰夫耶茨的想法.如果你稍微修改它,它将完美地工作:

string regex = String.Format("[{0}]", Regex.Escape(new string(Path.GetInvalidFileNameChars())));
Regex removeInvalidChars = new Regex(regex, RegexOptions.Singleline | RegexOptions.Compiled | RegexOptions.CultureInvariant);
Run Code Online (Sandbox Code Playgroud)

改进只是为了逃避自动生成的正则表达式.


小智 11

这是一个应该有助于.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, "");
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 8

上面的大多数解决方案将路径和文件名的非法字符组合在一起是错误的(即使两个调用当前都返回相同的字符集).我首先在路径和文件名中拆分路径+文件名,然后将相应的设置应用于它们,然后再将两者合并.

wvd_vegt


Max*_*nce 6

如果删除或替换单个字符的无效字符,则可能发生冲突:

<abc -> abc
>abc -> abc
Run Code Online (Sandbox Code Playgroud)

这是一个避免这种情况的简单方法:

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;
}
Run Code Online (Sandbox Code Playgroud)

结果:

 <abc -> [1]abc
 >abc -> [2]abc
Run Code Online (Sandbox Code Playgroud)


Ale*_*y F 6

这似乎是 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());
    }
Run Code Online (Sandbox Code Playgroud)


mir*_*zus 5

抛出一个例外.

if ( fileName.IndexOfAny(Path.GetInvalidFileNameChars()) > -1 )
            {
                throw new ArgumentException();
            }
Run Code Online (Sandbox Code Playgroud)


Bac*_*cks 5

文件名不能包含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;
    }
}
Run Code Online (Sandbox Code Playgroud)

方法GetValidFileName将所有不正确的数据替换为_.


Sim*_*ant 5

如果您必须在项目中的许多地方使用该方法,您还可以创建一个扩展方法并在项目中的任何位置调用它以获取字符串。

 public static class StringExtension
    {
        public static string RemoveInvalidChars(this string originalString)
        {            
            string finalString=string.Empty;
            if (!string.IsNullOrEmpty(originalString))
            {
                return string.Concat(originalString.Split(Path.GetInvalidFileNameChars()));
            }
            return finalString;            
        }
    }
Run Code Online (Sandbox Code Playgroud)

您可以将上述扩展方法调用为:

string illegal = "\"M<>\"\\a/ry/ h**ad:>> a\\/:*?\"<>| li*tt|le|| la\"mb.?";
string afterIllegalChars = illegal.RemoveInvalidChars();
Run Code Online (Sandbox Code Playgroud)