How do I check if a filename matches a wildcard pattern

You could translate the wildcards into a regular expression:

*.txt -> ^.+\.txt$

POS??.dat _> ^POS..\.dat$

Use the Regex.Escape method to escape the characters that are not wildcars into literal strings for the pattern (e.g. converting ".txt" to "\.txt").

The wildcard * translates into .+, and ? translates into .

Put ^ at the beginning of the pattern to match the beginning of the string, and $ at the end to match the end of the string.

Now you can use the Regex.IsMatch method to check if a file name matches the pattern.


Just call the Windows API function PathMatchSpecExW().

[Flags]
public enum MatchPatternFlags : uint
{
    Normal          = 0x00000000,   // PMSF_NORMAL
    Multiple        = 0x00000001,   // PMSF_MULTIPLE
    DontStripSpaces = 0x00010000    // PMSF_DONT_STRIP_SPACES
}

class FileName
{
    [DllImport("Shlwapi.dll", SetLastError = false)]
    static extern int PathMatchSpecExW([MarshalAs(UnmanagedType.LPWStr)] string file,
                                       [MarshalAs(UnmanagedType.LPWStr)] string spec,
                                       MatchPatternFlags flags);

    /*******************************************************************************
    * Function:     MatchPattern
    *
    * Description:  Matches a file name against one or more file name patterns.
    *
    * Arguments:    file - File name to check
    *               spec - Name pattern(s) to search foe
    *               flags - Flags to modify search condition (MatchPatternFlags)
    *
    * Return value: Returns true if name matches the pattern.
    *******************************************************************************/

    public static bool MatchPattern(string file, string spec, MatchPatternFlags flags)
    {
        if (String.IsNullOrEmpty(file))
            return false;

        if (String.IsNullOrEmpty(spec))
            return true;

        int result = PathMatchSpecExW(file, spec, flags);

        return (result == 0);
    }
}

I have a complete answer in code for you that's 95% like FindFiles(string).

The 5% that isn't there is the short names/long names behavior in the second note on the MSDN documentation for this function.

If you would still like to get that behavior, you'll have to complete a computation of the short name of each string you have in the input array, and then add the long name to the collection of matches if either the long or short name matches the pattern.

Here is the code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace FindFilesRegEx
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] names = { "hello.t", "HelLo.tx", "HeLLo.txt", "HeLLo.txtsjfhs", "HeLLo.tx.sdj", "hAlLo20984.txt" };
            string[] matches;
            matches = FindFilesEmulator("hello.tx", names);
            matches = FindFilesEmulator("H*o*.???", names);
            matches = FindFilesEmulator("hello.txt", names);
            matches = FindFilesEmulator("lskfjd30", names);
        }

        public string[] FindFilesEmulator(string pattern, string[] names)
        {
            List<string> matches = new List<string>();
            Regex regex = FindFilesPatternToRegex.Convert(pattern);
            foreach (string s in names)
            {
                if (regex.IsMatch(s))
                {
                    matches.Add(s);
                }
            }
            return matches.ToArray();
        }

        internal static class FindFilesPatternToRegex
        {
            private static Regex HasQuestionMarkRegEx   = new Regex(@"\?", RegexOptions.Compiled);
            private static Regex IllegalCharactersRegex  = new Regex("[" + @"\/:<>|" + "\"]", RegexOptions.Compiled);
            private static Regex CatchExtentionRegex    = new Regex(@"^\s*.+\.([^\.]+)\s*$", RegexOptions.Compiled);
            private static string NonDotCharacters      = @"[^.]*";
            public static Regex Convert(string pattern)
            {
                if (pattern == null)
                {
                    throw new ArgumentNullException();
                }
                pattern = pattern.Trim();
                if (pattern.Length == 0)
                {
                    throw new ArgumentException("Pattern is empty.");
                }
                if(IllegalCharactersRegex.IsMatch(pattern))
                {
                    throw new ArgumentException("Pattern contains illegal characters.");
                }
                bool hasExtension = CatchExtentionRegex.IsMatch(pattern);
                bool matchExact = false;
                if (HasQuestionMarkRegEx.IsMatch(pattern))
                {
                    matchExact = true;
                }
                else if(hasExtension)
                {
                    matchExact = CatchExtentionRegex.Match(pattern).Groups[1].Length != 3;
                }
                string regexString = Regex.Escape(pattern);
                regexString = "^" + Regex.Replace(regexString, @"\\\*", ".*");
                regexString = Regex.Replace(regexString, @"\\\?", ".");
                if(!matchExact && hasExtension)
                {
                    regexString += NonDotCharacters;
                }
                regexString += "$";
                Regex regex = new Regex(regexString, RegexOptions.Compiled | RegexOptions.IgnoreCase);
                return regex;
            }
        }
    }
}

You can simply do this. You do not need regular expressions.

using Microsoft.VisualBasic.CompilerServices;

if (Operators.LikeString("pos123.txt", "pos?23.*", CompareMethod.Text))
{
  Console.WriteLine("Filename matches pattern");
}

Or, in VB.Net,

If "pos123.txt" Like "pos?23.*" Then
  Console.WriteLine("Filename matches pattern")
End If

In c# you could simulate this with an extension method. It wouldn't be exactly like VB Like, but it would be like...very cool.

Tags:

.Net

Regex