Click here to Skip to main content
15,867,453 members
Articles / Programming Languages / C++

A Simple Wildcard Matching Function

Rate me:
Please Sign up or sign in to vote.
4.83/5 (17 votes)
28 Apr 2011CPOL1 min read 68.5K   595   26   25
A Simple Wildcard Matching Function

Introduction

Simple wild card matching with ? and * is something that we use in our every day work just when opening a command prompt and using DIR and DEL.

But how can this be done correctly in your program?

OK, you can use regular expressions, and I recommend this when you already use a bunch of Boost, tr1 or STL stuff in your code. But sometimes, I like it easy and simple without tons of library code in the background. And because I saw a lot of wrong and in complex queries "wrong/failing" code, I just offer this small algorithm here.

Background

This wildcard matching function works just the same way as you expect and know it from the CMD.EXE DIR command.

Using the Code

The function just takes the string to check as a first argument and the mask with or without any wildcard characters as a second argument.
It returns true if the strings match and false if not. It isn't spectacular.

The characters ? and * are treated as wildcards.
A ? character matches exactly one character and doesn't match an empty string.
A * character matches any sequence of characters and an empty string too.
Other characters are compared caseless. I use CharUpper and convert them to uppercase to perform this task. Feel free to use your own favorite way.

Because * matches any character sequence and an empty string WildcardMatch(_T(""),_T("*")) returns true.

The function itself and the documentation is easy and straight forward:

C++
//////////////////////////////////////////////////////////////////////////
//    WildcardMatch
//        pszString    - Input string to match
//        pszMatch    - Match mask that may contain wildcards like ? and *
//    
//        A ? sign matches any character, except an empty string.
//        A * sign matches any string inclusive an empty string.
//        Characters are compared caseless.

bool WildcardMatch(const TCHAR *pszString, const TCHAR *pszMatch)
{
    // We have a special case where string is empty ("") and the mask is "*".
    // We need to handle this too. So we can't test on !*pszString here.
    // The loop breaks when the match string is exhausted.
    while (*pszMatch)
    {
        // Single wildcard character
        if (*pszMatch==_T('?'))
        {
            // Matches any character except empty string
            if (!*pszString)
                return false;

            // OK next
            ++pszString;
            ++pszMatch;
        }
        else if (*pszMatch==_T('*'))
        {
            // Need to do some tricks.

            // 1. The wildcard * is ignored. 
            //    So just an empty string matches. This is done by recursion.
            //      Because we eat one character from the match string, the
            //      recursion will stop.
            if (WildcardMatch(pszString,pszMatch+1))
                // we have a match and the * replaces no other character
                return true;

            // 2. Chance we eat the next character and try it again, with a
            //    wildcard * match. This is done by recursion. Because we eat
            //      one character from the string, the recursion will stop.
            if (*pszString && WildcardMatch(pszString+1,pszMatch))
                return true;

            // Nothing worked with this wildcard.
            return false;
        }
        else
        {
            // Standard compare of 2 chars. Note that *pszSring might be 0
            // here, but then we never get a match on *pszMask that has always
            // a value while inside this loop.
            if (::CharUpper(MAKEINTRESOURCE(MAKELONG(*pszString++,0)))
		!=::CharUpper(MAKEINTRESOURCE(MAKELONG(*pszMatch++,0))))
                return false;
        }
    }

    // Have a match? Only if both are at the end...
    return !*pszString && !*pszMatch;
}

History

  • 28 April, 2011 -- Version 1.0

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Germany Germany
MVP for C++ in Germany from 2000 up to 2015.
Developer since 1979, working with C/C++ since 1982
Started with Windows development in 1990

Love my bicycles (specially my recumbent) and geocaching
http://blog.m-ri.de/index.php/category/real-life/fahrrad/

Comments and Discussions

 
GeneralMy vote of 5 Pin
Matth Moestl25-Oct-15 7:25
professionalMatth Moestl25-Oct-15 7:25 
GeneralMy vote of 5 Pin
Jan Heckman25-Apr-15 2:03
professionalJan Heckman25-Apr-15 2:03 
QuestionAll of the "dehydrated" versions break... Pin
F. Scott Deaver5-Apr-15 3:24
professionalF. Scott Deaver5-Apr-15 3:24 
QuestionHelp on linux version of wild card matching Pin
Member 1103790026-Aug-14 14:38
Member 1103790026-Aug-14 14:38 
GeneralNice! Pin
Tesfamichael G.23-Mar-14 20:32
Tesfamichael G.23-Mar-14 20:32 
GeneralMy vote of 5 Pin
Jason Newland27-Jul-13 9:52
Jason Newland27-Jul-13 9:52 
GeneralThank you Pin
Jason Newland27-Jul-13 9:50
Jason Newland27-Jul-13 9:50 
GeneralMy vote of 5 Pin
IngKatina21-Mar-13 6:23
IngKatina21-Mar-13 6:23 
GeneralHere is a dehydrated version:) [modified] Pin
Dezhi Zhao28-Apr-11 7:47
Dezhi Zhao28-Apr-11 7:47 
GeneralRe: Here is a dehydrated version:) Pin
Martin Richter [rMVP C++]28-Apr-11 10:02
Martin Richter [rMVP C++]28-Apr-11 10:02 
GeneralRe: Here is a dehydrated version:) Pin
Dezhi Zhao28-Apr-11 12:21
Dezhi Zhao28-Apr-11 12:21 
GeneralRe: Here is a dehydrated version:) Pin
Sanmayce29-Nov-13 8:04
Sanmayce29-Nov-13 8:04 
GeneralRe: Here is a dehydrated version:) Pin
akemper28-Apr-11 10:45
akemper28-Apr-11 10:45 
GeneralRe: Here is a dehydrated version:) Pin
Dezhi Zhao28-Apr-11 12:49
Dezhi Zhao28-Apr-11 12:49 
GeneralRe: Here is a dehydrated version:) Pin
Martin Richter [rMVP C++]28-Apr-11 21:59
Martin Richter [rMVP C++]28-Apr-11 21:59 
GeneralRe: Here is a dehydrated version:) PinPopular
Dezhi Zhao29-Apr-11 14:05
Dezhi Zhao29-Apr-11 14:05 
GeneralRe: Here is a dehydrated version:) Pin
Franc Morales29-May-13 19:21
Franc Morales29-May-13 19:21 
GeneralRe: Here is a dehydrated version:) Pin
Dezhi Zhao29-May-13 19:42
Dezhi Zhao29-May-13 19:42 
GeneralRe: Here is a dehydrated version:) Pin
Jason Newland27-Jul-13 9:46
Jason Newland27-Jul-13 9:46 
GeneralRe: Here is a dehydrated version:) [modified] Pin
IngKatina21-Mar-13 8:59
IngKatina21-Mar-13 8:59 
GeneralRe: Here is a dehydrated version:) [modified] Pin
Dezhi Zhao21-Mar-13 14:17
Dezhi Zhao21-Mar-13 14:17 
GeneralRe: Here is a dehydrated version:) [modified] Pin
IngKatina22-Mar-13 2:24
IngKatina22-Mar-13 2:24 
GeneralRe: Here is a dehydrated version:) [modified] Pin
Dezhi Zhao22-Mar-13 5:42
Dezhi Zhao22-Mar-13 5:42 
Now I think I know what you meant Smile | :)
However, I think you did an invalid test. I just run the same test and the results from the original and the dehydrated are the same, 1.
So, please check if you messed up the dehydrated version somehow.

In my test, I just copied and pasted the code snip of the dehydrated from my original post in this thread.
Please do the same thing, run your tests again and let us know the results. Thanks

modified 22-Mar-13 14:13pm.

GeneralRe: Here is a dehydrated version:) [modified] Pin
Jason Newland27-Jul-13 9:47
Jason Newland27-Jul-13 9:47 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.