Does anyone know if there's an equivalent functionality to the Windows API function PathMatchSpec()
in .NET?
4 Answers
I'm not aware of a method built in to .NET however it's trivial to duplicate with a Regex:
public static bool PathMatchSpec(String path, String spec)
{
String specAsRegex = Regex.Escape(spec).Replace("\\*", ".*").Replace("\\?", ".") + "$";
return Regex.IsMatch(path, specAsRegex);
}
Obviously this assumes the System.Text.RegularExpressions namespace is referenced. If you're going to do this alot with the same spec you could cache the Regex as well.
EDIT TO ADD: P/Invoke is indeed an option, but the signature for PathMatchSpec indicates it takes an ANSI string, so you'd be incurring a character set conversion for each invocation. Keep that in mind if you go that route. In that case PathMatchSpecEx would probably be preferable.

- 2,569
- 1
- 19
- 30
-
Cool... didn't know of the Escape() method myself; would definitely simplify part of my solution ;) – jerryjvl Jun 05 '09 at 06:15
If you can't get the functionality using Regular Expressions (I believe this is the case) how about using PathMatchSpec() through PInvoke?
http://www.pinvoke.net/default.aspx/shlwapi/PathMatchSpec.html

- 53,046
- 9
- 139
- 151
You can try How to implement glob in C# or of course there's the PInvoke route if necessary.

- 1
- 1

- 278,309
- 50
- 514
- 539
In short... not that I know of... but maybe this can help you along (note, a bit lengthier than you might want, but it has served me well):
sealed public class WildcardMatch
{
private static Regex wildcardFinder = new Regex(@"(?<wildcards>\?+|\*+)", RegexOptions.Compiled | RegexOptions.Singleline);
private Regex wildcardRegex;
public WildcardMatch(string wildcardFormat) : this(wildcardFormat, false) { }
public WildcardMatch(string wildcardFormat, bool ignoreCase)
{
if (wildcardFormat == null)
throw new ArgumentNullException("wildcardFormat");
StringBuilder patternBuilder = new StringBuilder("^");
MatchCollection matches = this.wildcardFinder.Matches(wildcardFormat);
string[] split = this.wildcardFinder.Split(wildcardFormat);
for (int ix = 0; ix < split.Length; ix++)
{
// Even indexes are literal text, odd indexes correspond to matches
if (ix % 2 == 0)
patternBuilder.Append(Regex.Escape(split[ix]));
else
{
// Matches must be substituted with Regex control characters
string wildcards = matches[ix / 2].Groups["wildcards"].Value;
if (wildcards.StartsWith("*", StringComparison.Ordinal))
patternBuilder.Append("(.*)");
else
patternBuilder.AppendFormat(CultureInfo.InvariantCulture, "({0})", wildcards.Replace('?', '.'));
}
}
patternBuilder.Append("$");
this.wildcardRegex = new Regex(
patternBuilder.ToString(),
RegexOptions.Singleline | (ignoreCase ? RegexOptions.IgnoreCase : RegexOptions.None));
}
public bool IsMatch(string value)
{
if (value == null)
return false;
return this.wildcardRegex.IsMatch(value);
}
public IEnumerable<string> ExtractMatches(string value)
{
if (value == null)
yield break;
Match match = this.wildcardRegex.Match(value);
if (!match.Success)
yield break;
for (int ix = 1; ix < match.Groups.Count; ix++)
yield return match.Groups[ix].Value;
}
}

- 19,723
- 7
- 40
- 55
-
Note that by using anelsons 'Regex.Escape()' the escaping code can definitely be simplified. – jerryjvl Jun 05 '09 at 06:15