How to determine if a File Matches a File Mask?

后端 未结 13 1640
清歌不尽
清歌不尽 2020-12-14 07:14

I need to decide whether file name fits to file mask. The file mask could contain * or ? characters. Is there any simple solution for this?

bool bFits = Fits         


        
相关标签:
13条回答
  • 2020-12-14 07:33

    My version, which supports ** wild card:

        static Regex FileMask2Regex(string mask)
        {
            var sb = new StringBuilder(mask);
    
            // hide wildcards
            sb.Replace("**", "affefa0d52e84c2db78f5510117471aa-StarStar");
            sb.Replace("*", "affefa0d52e84c2db78f5510117471aa-Star");
            sb.Replace("?", "affefa0d52e84c2db78f5510117471aa-Question");
            sb.Replace("/", "affefa0d52e84c2db78f5510117471aa-Slash");
            sb.Replace("\\", "affefa0d52e84c2db78f5510117471aa-Slash");
    
            sb = new StringBuilder(Regex.Escape(sb.ToString()));
    
            // unhide wildcards
            sb.Replace("affefa0d52e84c2db78f5510117471aa-StarStar", @".*");
            sb.Replace("affefa0d52e84c2db78f5510117471aa-Star", @"[^/\\]*");
            sb.Replace("affefa0d52e84c2db78f5510117471aa-Question", @"[^/\\]");
            sb.Replace("affefa0d52e84c2db78f5510117471aa-Slash", @"[/\\]");
    
            sb.Append("$");
    
            // allowed to have prefix
            sb.Insert(0, @"^(?:.*?[/\\])?");
    
            return new Regex(sb.ToString(), RegexOptions.IgnoreCase);
        }
    
    0 讨论(0)
  • 2020-12-14 07:34

    Many people don't know that, but .NET includes an internal class, called "PatternMatcher" (under the "System.IO" namespace).

    This static class contains only 1 method: public static bool StrictMatchPattern(string expression, string name)

    This method is used by .net whenever it needs to compare files with wildcard (FileSystemWatcher, GetFiles(), etc)

    Using reflector, I exposed the code here. Didn't really go through it to understand how it works, but it works great,

    So this is the code for anyone who doesn't want to work with the inefficient RegEx way:

    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);
        }
    }
    
    0 讨论(0)
  • 2020-12-14 07:37

    Try this:

    private bool FitsMask(string sFileName, string sFileMask)
    {
        Regex mask = new Regex(sFileMask.Replace(".", "[.]").Replace("*", ".*").Replace("?", "."));
        return mask.IsMatch(sFileName);
    }
    
    0 讨论(0)
  • 2020-12-14 07:39

    Use WildCardPattern class from System.Management.Automation available as NuGet package or in Windows PowerShell SDK.

    WildcardPattern pattern = new WildcardPattern("my*.txt");
    bool fits = pattern.IsMatch("myfile.txt");
    
    0 讨论(0)
  • 2020-12-14 07:48

    Fastest version of the previously proposed function:

        public static bool FitsMasks(string filePath, params string[] fileMasks)
                // or
        public static Regex FileMasksToRegex(params string[] fileMasks)
        {
            if (!_maskRegexes.ContainsKey(fileMasks))
            {
                StringBuilder sb = new StringBuilder("^");
                bool first = true;
                foreach (string fileMask in fileMasks)
                {
                    if(first) first =false; else sb.Append("|");
                    sb.Append('(');
                    foreach (char c in fileMask)
                    {
                        switch (c)
                        {
                            case '*': sb.Append(@".*"); break;
                            case '?': sb.Append(@"."); break;
                            default:
                                    sb.Append(Regex.Escape(c.ToString()));
                                break;
                        }
                    }
                    sb.Append(')');
                }
                sb.Append("$");
                _maskRegexes[fileMasks] = new Regex(sb.ToString(), RegexOptions.IgnoreCase);
            }
            return _maskRegexes[fileMasks].IsMatch(filePath);
                        // or
            return _maskRegexes[fileMasks];
        }
        static readonly Dictionary<string[], Regex> _maskRegexes = new Dictionary<string[], Regex>(/*unordered string[] comparer*/);
    

    Notes:

    1. Re-using Regex objects.
    2. Using StringBuilder to optimize Regex creation (multiple .Replace() calls are slow).
    3. Multiple masks, combined with OR.
    4. Another version returning the Regex.
    0 讨论(0)
  • 2020-12-14 07:49

    I didn't want to copy the source code and like @frankhommers I came up with a reflection based solution.

    Notice the code comment about the use of wildcards in the name argument I found in the reference source.

        public static class PatternMatcher
        {
            static MethodInfo strictMatchPatternMethod;
            static PatternMatcher()
            {
                var typeName = "System.IO.PatternMatcher";
                var methodName = "StrictMatchPattern";
                var assembly = typeof(Uri).Assembly;
                var type = assembly.GetType(typeName, true);
                strictMatchPatternMethod = type.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public) ?? throw new MissingMethodException($"{typeName}.{methodName} not found");
            }
    
            /// <summary>
            /// Tells whether a given name matches the expression given with a strict (i.e. UNIX like) semantics.
            /// </summary>
            /// <param name="expression">Supplies the input expression to check against</param>
            /// <param name="name">Supplies the input name to check for.</param>
            /// <returns></returns>
            public static bool StrictMatchPattern(string expression, string name)
            {
                // https://referencesource.microsoft.com/#system/services/io/system/io/PatternMatcher.cs
                // If this class is ever exposed for generic use,
                // we need to make sure that name doesn't contain wildcards. Currently 
                // the only component that calls this method is FileSystemWatcher and
                // it will never pass a name that contains a wildcard.
                if (name.Contains('*')) throw new FormatException("Wildcard not allowed");
                return (bool)strictMatchPatternMethod.Invoke(null, new object[] { expression, name });
            }
        }
    
    0 讨论(0)
提交回复
热议问题