file name matching with wildcard

前端 未结 7 1628
闹比i
闹比i 2021-01-01 16:14

I need to implement something like my own file system. One operation would be the FindFirstFile. I need to check, if the caller passed something like ., sample*.cpp

7条回答
  •  野趣味
    野趣味 (楼主)
    2021-01-01 16:50

    There are quite a few such functions around. Here's a directory of various implementations, sorted into recursive and non-recursive, etc.

    In case you don't like the licensing there (or have trouble with the link, etc.) here's one possible implementation of a matching algorithm that at least closely approximates what Windows uses:

    #include 
    #include 
    
    bool match(char const *needle, char const *haystack) {
        for (; *needle != '\0'; ++needle) {
            switch (*needle) {
            case '?': 
                if (*haystack == '\0')
                    return false;
                ++haystack;
                break;
            case '*': {
                if (needle[1] == '\0')
                    return true;
                size_t max = strlen(haystack);
                for (size_t i = 0; i < max; i++)
                    if (match(needle + 1, haystack + i))
                        return true;
                return false;
            }
            default:
                if (*haystack != *needle)
                    return false;
                ++haystack;
            }
        }
        return *haystack == '\0';
    }
    
    #ifdef TEST
    #define CATCH_CONFIG_MAIN
    
    #include "catch.hpp"
    
    TEST_CASE("Matching", "[match]") {
        REQUIRE(match("a", "a") == true);
        REQUIRE(match("a", "b") == false);
        REQUIRE(match("a*", "a") == true);
        REQUIRE(match("a?", "a") == false);
        REQUIRE(match("a?", "ab") == true);
        REQUIRE(match("a*b", "ab") == true);
        REQUIRE(match("a*b", "acb") == true);
        REQUIRE(match("a*b", "abc") == false);
        REQUIRE(match("*a*??????a?????????a???????????????", 
            "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") == true);
    }
    
    #endif
    

    Since there was a discussion of complexity of some of the other answers, I'll note that I believe this has O(NM) complexity and O(M) storage use (where N is the size of the target string, and M is the size of the pattern).

    With @masterxilo's test pair:

    "*a*??????*a*?????????a???????????????", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
    

    ...this finds a match in approximately 3 microseconds on my machine. That is a lot slower than a typical pattern--most of my other tests run in about 300 nanoseconds or so on this particular machine.

    At the same time, @masterxilo's code takes approximately 11 microseconds to run on the same machine, so this is still around 3 to 4 times faster (not to mention being somewhat smaller and simpler).

提交回复
热议问题