如何确定文件是否与文件掩码匹配?

jin*_*ing 26 .net c# regex

我需要确定文件名是否适合文件掩码.文件掩码可以包含*或?字符.这有什么简单的解决方案吗?

bool bFits = Fits("myfile.txt", "my*.txt");

private bool Fits(string sFileName, string sFileMask)
    {
        ??? anything simple here ???
    }
Run Code Online (Sandbox Code Playgroud)

Mic*_*ens 25

我很高兴找到乔尔的答案 - 也节省了我一些时间!但是,我确实需要进行一些更改,以使该方法能够满足大多数用户的期望:

  • 我在第一个参数之前删除了'this'关键字.它在这里没有任何作用(尽管如果该方法是一个扩展方法,它可能是有用的,在这种情况下,它需要是公共的并包含在静态类中,并且本身是一个静态方法).
  • 我使正则表达式独立于案例以匹配标准的Windows通配符行为(例如,"c*.*"和"C*.*"都返回相同的结果).
  • 我在正则表达式中添加了开始和结束锚点,再次匹配标准的Windows通配符行为(例如,"stuff.txt"将匹配"stuff*"或"s*"或"s*.*"但不仅仅是"S").

private bool FitsMask(string fileName, string fileMask)
{
    Regex mask = new Regex(
        '^' + 
        fileMask
            .Replace(".", "[.]")
            .Replace("*", ".*")
            .Replace("?", ".")
        + '$',
        RegexOptions.IgnoreCase);
    return mask.IsMatch(fileName);
}
Run Code Online (Sandbox Code Playgroud)

2009.11.04更新:匹配几个面具中的一个

为了获得更大的灵活性,这里有一个基于原件的插件兼容方法.此版本允许您传递多个蒙版(因此第二个参数名称fileMasks上的复数)由线,逗号,竖线或空格分隔.我想它,这样我可以让用户把所希望ListBox中尽可能多的选择,然后选择匹配所有文件的任何人.请注意,某些控件(如ListBox)使用CR-LF进行换行,而其他控件(例如RichTextBox)仅使用LF - 这就是为什么"\ r \n"和"\n"都显示在拆分列表中.

private bool FitsOneOfMultipleMasks(string fileName, string fileMasks)
{
    return fileMasks
        .Split(new string[] {"\r\n", "\n", ",", "|", " "},
            StringSplitOptions.RemoveEmptyEntries)
        .Any(fileMask => FitsMask(fileName, fileMask));
}
Run Code Online (Sandbox Code Playgroud)

2009.11.17更新:处理fileMask输入更优雅

早期版本的FitsMask(我已经留下来进行比较)做得很公平,但由于我们将它视为正则表达式,如果它不是有效的正则表达式,它将抛出异常.解决方案是我们实际上希望输入fileMask中的任何正则表达式元字符都被视为文字,而不是元字符.但我们还是需要特别处理句号,星号和问号.因此,这个改进版的FitsMask安全地将这三个字符移开,将所有剩余的元字符转换为文字,然后以"正则表达式"形式将三个有趣的字符放回.

另一个小改进是根据标准Windows行为允许独立于案例.

private bool FitsMask(string fileName, string fileMask)
{
    string pattern =
         '^' + 
         Regex.Escape(fileMask.Replace(".", "__DOT__")
                         .Replace("*", "__STAR__")
                         .Replace("?", "__QM__"))
             .Replace("__DOT__", "[.]")
             .Replace("__STAR__", ".*")
             .Replace("__QM__", ".")
         + '$';
    return new Regex(pattern, RegexOptions.IgnoreCase).IsMatch(fileName);
}
Run Code Online (Sandbox Code Playgroud)

2010.09.30更新:一路上,激情随之而来......

我之前没有更新过这个问题,但这些参考资料可能会引起读者的兴趣:

  • 我嵌入FitsMask方法形象地称为一个一个的WinForms用户控件的心脏文件匹配 --see的API 在这里.
  • 然后,我写了一篇文章,其中介绍了在Simple-Talk.com上发布的FileMask控件,名为使用LINQ Lambda表达式来设计可定制的通用组件.(虽然该方法本身不使用LINQ,但FileMask用户控件确实如此,因此该文章的标题.)

  • @Nyderguds提出了另一种解决方案,我建议人们向下滚动查找. (2认同)

Joe*_*orn 22

试试这个:

private bool FitsMask(string sFileName, string sFileMask)
{
    Regex mask = new Regex(sFileMask.Replace(".", "[.]").Replace("*", ".*").Replace("?", "."));
    return mask.IsMatch(sFileName);
}
Run Code Online (Sandbox Code Playgroud)

  • 如果你正在看窗户那么这是不对的.例如,给定掩码"*.asp"它将匹配"foo.asp"和"foo.aspx"和"foo.aspxx",但是给定掩码"*.aspx"它将仅掩盖"foo.aspx".三字符扩展有特殊规则. (3认同)
  • 好的,现在让我们尝试将文件 file$name.com 与掩码 * $ *.com 进行匹配。正则表达式将为 .*$.*[.]com 并且它不会匹配文件名。因此,您必须转义所有这些在正则表达式中具有特殊含义的有效字符:[]()^$+=!{,} (2认同)
  • @Nyderguds提出了一种替代解决方案,我建议人们向下滚动以查找。 (2认同)

Nis*_*sim 13

许多人不知道,但.NET包含一个内部类,称为"PatternMatcher"(在"System.IO"命名空间下).

这个静态类只包含1个方法: public static bool StrictMatchPattern(string expression, string name)

只要需要将文件与通配符(FileSystemWatcher,GetFiles()等)进行比较,.net就会使用此方法

使用反射器,我在这里公开了代码.没有真正了解它是如何工作的,但是效果很好,

所以这是任何不想使用效率低下的RegEx方式的人的代码:

public static class PatternMatcher
{
    // Fields
    private const char ANSI_DOS_QM = '<';
    private const char ANSI_DOS_STAR = '>';
    private const char DOS_DOT = '"';
    private const int MATCHES_ARRAY_SIZE = 16;

    // Methods
    public static bool StrictMatchPattern(string expression, string name)
    {
        expression = expression.ToLowerInvariant();
        name = name.ToLowerInvariant();
        int num9;
        char ch = '\0';
        char ch2 = '\0';
        int[] sourceArray = new int[16];
        int[] numArray2 = new int[16];
        bool flag = false;
        if (((name == null) || (name.Length == 0)) || ((expression == null) || (expression.Length == 0)))
        {
            return false;
        }
        if (expression.Equals("*") || expression.Equals("*.*"))
        {
            return true;
        }
        if ((expression[0] == '*') && (expression.IndexOf('*', 1) == -1))
        {
            int length = expression.Length - 1;
            if ((name.Length >= length) && (string.Compare(expression, 1, name, name.Length - length, length, StringComparison.OrdinalIgnoreCase) == 0))
            {
                return true;
            }
        }
        sourceArray[0] = 0;
        int num7 = 1;
        int num = 0;
        int num8 = expression.Length * 2;
        while (!flag)
        {
            int num3;
            if (num < name.Length)
            {
                ch = name[num];
                num3 = 1;
                num++;
            }
            else
            {
                flag = true;
                if (sourceArray[num7 - 1] == num8)
                {
                    break;
                }
            }
            int index = 0;
            int num5 = 0;
            int num6 = 0;
            while (index < num7)
            {
                int num2 = (sourceArray[index++] + 1) / 2;
                num3 = 0;
            Label_00F2:
                if (num2 != expression.Length)
                {
                    num2 += num3;
                    num9 = num2 * 2;
                    if (num2 == expression.Length)
                    {
                        numArray2[num5++] = num8;
                    }
                    else
                    {
                        ch2 = expression[num2];
                        num3 = 1;
                        if (num5 >= 14)
                        {
                            int num11 = numArray2.Length * 2;
                            int[] destinationArray = new int[num11];
                            Array.Copy(numArray2, destinationArray, numArray2.Length);
                            numArray2 = destinationArray;
                            destinationArray = new int[num11];
                            Array.Copy(sourceArray, destinationArray, sourceArray.Length);
                            sourceArray = destinationArray;
                        }
                        if (ch2 == '*')
                        {
                            numArray2[num5++] = num9;
                            numArray2[num5++] = num9 + 1;
                            goto Label_00F2;
                        }
                        if (ch2 == '>')
                        {
                            bool flag2 = false;
                            if (!flag && (ch == '.'))
                            {
                                int num13 = name.Length;
                                for (int i = num; i < num13; i++)
                                {
                                    char ch3 = name[i];
                                    num3 = 1;
                                    if (ch3 == '.')
                                    {
                                        flag2 = true;
                                        break;
                                    }
                                }
                            }
                            if ((flag || (ch != '.')) || flag2)
                            {
                                numArray2[num5++] = num9;
                                numArray2[num5++] = num9 + 1;
                            }
                            else
                            {
                                numArray2[num5++] = num9 + 1;
                            }
                            goto Label_00F2;
                        }
                        num9 += num3 * 2;
                        switch (ch2)
                        {
                            case '<':
                                if (flag || (ch == '.'))
                                {
                                    goto Label_00F2;
                                }
                                numArray2[num5++] = num9;
                                goto Label_028D;

                            case '"':
                                if (flag)
                                {
                                    goto Label_00F2;
                                }
                                if (ch == '.')
                                {
                                    numArray2[num5++] = num9;
                                    goto Label_028D;
                                }
                                break;
                        }
                        if (!flag)
                        {
                            if (ch2 == '?')
                            {
                                numArray2[num5++] = num9;
                            }
                            else if (ch2 == ch)
                            {
                                numArray2[num5++] = num9;
                            }
                        }
                    }
                }
            Label_028D:
                if ((index < num7) && (num6 < num5))
                {
                    while (num6 < num5)
                    {
                        int num14 = sourceArray.Length;
                        while ((index < num14) && (sourceArray[index] < numArray2[num6]))
                        {
                            index++;
                        }
                        num6++;
                    }
                }
            }
            if (num5 == 0)
            {
                return false;
            }
            int[] numArray4 = sourceArray;
            sourceArray = numArray2;
            numArray2 = numArray4;
            num7 = num5;
        }
        num9 = sourceArray[num7 - 1];
        return (num9 == num8);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我是第二个Nyeguds,它应该是System.IO.Path类的一部分! (4认同)
  • 现在我们有源代码:https://referencesource.microsoft.com/#System/services/io/system/io/PatternMatcher.cs (3认同)
  • 那是......令人费解的愚蠢.哇.他们为什么不暴露这个?X_X (2认同)

Nye*_*uds 11

这些答案似乎都没有做到这一点,并且msorens的不必要的复杂.这个应该工作得很好:

public static Boolean Fits(string sFileName, string sFileMask)
{
    String convertedMask = "^" + Regex.Escape(sFileMask).Replace("\\*", ".*").Replace("\\?", ".") + "$";
    Regex regexMask = new Regex(convertedMask, RegexOptions.IgnoreCase);
    return regexMask.IsMatch(sFileName)
}
Run Code Online (Sandbox Code Playgroud)

这可以确保掩码中可能的正则表达式字符被转义,替换\*和\?,并用^和$围绕它以标记边界.

当然,在大多数情况下,简单地将它变成一个FileMaskToRegex返回Regex对象的工具函数会更有用,所以你只需要一次,然后可以创建一个循环来检查文件列表中的所有字符串.

public static Regex FileMaskToRegex(string sFileMask)
{
    String convertedMask = "^" + Regex.Escape(sFileMask).Replace("\\*", ".*").Replace("\\?", ".") + "$";
    return new Regex(convertedMask, RegexOptions.IgnoreCase);
}
Run Code Online (Sandbox Code Playgroud)