имя файла, соответствующее подстановочному знаку

Я должен реализовать что-то как своя собственная файловая система. Одной операцией был бы FindFirstFile. Я должен проверить, если бы вызывающая сторона передала что-то как., sample*.cpp или около этого. Моя реализация "файловой системы" предоставляет список "имен файлов" как массив char*.

Есть ли какая-либо функция Windows или какой-либо исходный код, который реализует это имя файла, соответствующее?

16
задан harper 21 July 2010 в 14:37
поделиться

4 ответа

Таких функций довольно много. Вот каталог различных реализаций, отсортированный по рекурсивным и нерекурсивным и т. Д.

Если вам не нравится там лицензирование (или у вас проблемы со ссылкой и т. Д.), Вот один из возможных реализация алгоритма сопоставления, который, по крайней мере, близко приближается к тому, что использует Windows:

#include <string.h>
#include <iostream>

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

Поскольку обсуждалась сложность некоторых других ответов, я отмечу, что я считаю, что это имеет сложность O (NM) и O (M) использование хранилища (где N - размер целевой строки, а M - размер шаблона).

С тестовой парой @ masterxilo:

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

... на моей машине это находит совпадение примерно за 3 микросекунды. Этот намного медленнее, чем типичный образец - большинство других моих тестов выполняются примерно за 300 наносекунд или около того на этой конкретной машине.

В то же время код @ masterxilo запускается на той же машине примерно за 11 микросекунд, так что он все равно примерно в 3-4 раза быстрее (не говоря уже о том, что он несколько меньше и проще).

14
ответ дан 30 November 2019 в 15:51
поделиться

Для сопоставления имен с помощью '*' и '?' попробуйте следующее (если вы хотите избежать boost, используйте std::tr1::regex):

#include <boost/regex.hpp>
#include <boost/algorithm/string/replace.hpp>

using std::string;

bool MatchTextWithWildcards(const string &text, string wildcardPattern, bool caseSensitive /*= true*/)
{
    // Escape all regex special chars
    EscapeRegex(wildcardPattern);

    // Convert chars '*?' back to their regex equivalents
    boost::replace_all(wildcardPattern, "\\?", ".");
    boost::replace_all(wildcardPattern, "\\*", ".*");

    boost::regex pattern(wildcardPattern, caseSensitive ? Regex::normal : regex::icase);

    return regex_match(text, pattern);
}

void EscapeRegex(string &regex)
{
    boost::replace_all(regex, "\\", "\\\\");
    boost::replace_all(regex, "^", "\\^");
    boost::replace_all(regex, ".", "\\.");
    boost::replace_all(regex, "$", "\\$");
    boost::replace_all(regex, "|", "\\|");
    boost::replace_all(regex, "(", "\\(");
    boost::replace_all(regex, ")", "\\)");
    boost::replace_all(regex, "{", "\\{");
    boost::replace_all(regex, "{", "\\}");
    boost::replace_all(regex, "[", "\\[");
    boost::replace_all(regex, "]", "\\]");
    boost::replace_all(regex, "*", "\\*");
    boost::replace_all(regex, "+", "\\+");
    boost::replace_all(regex, "?", "\\?");
    boost::replace_all(regex, "/", "\\/");
}
15
ответ дан 30 November 2019 в 15:51
поделиться

Посмотрите на POSIX-функции fnmatch, glob и wordexp.

8
ответ дан 30 November 2019 в 15:51
поделиться

PathMatchSpec. Хотя он страдает от ограничения MAX_PATH (т.е. может принимать не более 260 символов). Возможно, вам будет лучше реализовать свой собственный матчер; это не очень много кода.

1
ответ дан 30 November 2019 в 15:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: