前言

在實際應用中,系統經常需要對一組標識符進行快速匹配。用户可能希望通過通配符模式靈活指定匹配規則,而不是手動列出每一個具體 ID。

其中通配符解釋如下:

通配符含義示例可匹配的字符串?匹配任意單個字符AB?DAB1D, ABXD*匹配任意長度(0~N)的任意字符12*891289, 123456789, 12ABCD89

場景示例

  • 用户希望匹配函數 ID:123411234212343
  • 手動列出所有 ID 很繁瑣
  • 希望使用通配符:1234?1234* 或 123?4*9 等自動匹配符合規則的 ID

因此,需要一個 高效、靈活、安全的通配符匹配算法,既支持整數也支持字符串。


算法設計

整體設計思路:

[用户輸入模式串]
           │
           ▼
     [去除空白字符]
           │
           ▼
     [指定分隔符(默認逗號)拆分模式串]
           │
           ▼
    [分類存儲模式]
   ┌───────────────┐
   │精確匹配集合   │
   │? 模式集合     │
   │* 模式集合     │
   └───────────────┘
           │
           ▼
   [用户輸入待匹配值] ──► 整數? ──► 轉字符串
           │
           ▼
       [匹配順序]
   ┌─────┬───────┬─────┐
   │精確匹配│? 模式 │* 模式│
   └─────┴───────┴─────┘
           │
           ▼
   匹配成功? → true
   否 → 返回 false

説明:

  1. 模式預處理(SetFuncIDs / ParseFuncIDs)
  • 將輸入的模式字符串拆分成若干條規則(支持指定,默認逗號分隔)
  • 去除空格、製表符等空白字符(NormalizeAndRemoveSpaces)
  • 將模式按類型分類存儲:
  • 精確匹配集合m_usExactMatchSet):沒有通配符的字符串
  • ? 模式集合m_mapQuestionPatterns):按長度分組存儲,便於快速篩選
  • * 模式集合m_vsWildcardPatterns):存儲帶 * 的複雜模式
  1. 匹配流程(IsMatch / IsMatchInternal)
  • 輸入可以是多種類型:int32_t/uint32_t/int64_t/uint64_t/std::string/const char*
  • 對整型輸入:
  • 通過 snprintf 將數字轉為字符串
  • 檢查返回值是否異常或超出緩衝區
  • 對字符串輸入:
  • 直接使用原字符串或 c_str()
  • 匹配順序
  • 精確匹配集合:直接哈希查找(O(1))
  • ? 模式集合:按長度篩選,然後逐字符匹配
  • * 模式集合:雙指針回溯匹配(WildcardMatch)

C++代碼示例

#include <string>
#include <vector>
#include <unordered_set>
#include <map>
#include <cstdint>
#include <iostream>
#include <new>
#include <cstring>
#include <cctype>    // for isspace
#include <algorithm> // for remove_if
#include <cinttypes> // 提供 PRId32 / PRIu32

const uint8_t APPEND_FUNCIDS_MODE = 1 << 2; // 追加模式標誌位

/**
 * @brief FuncMatcher 類用於匹配函數 ID 是否符合預定義的通配規則。
 *
 * 通配符支持規則:
 *   - '?' 匹配任意單個字符
 *   - '*' 匹配任意多個字符(包括空字符)
 */
class FuncMatcher
{
public:
    /**
     * @brief 構造函數,解析並緩存通配符規則。
     */
    explicit FuncMatcher() noexcept
    {
        ClearAll();
        m_nExceptionCount = 0;
    }

    /**
     * @brief 通用匹配接口,支持 int64_t
     * @param i64FuncID 待匹配的數值
     * @return true 如果匹配成功,否則 false
     */
    bool IsMatch(const int64_t i64FuncID) const
    {
        char buf[21]; // 符號 + 19位 + '\0'
        int len = snprintf(buf, sizeof(buf), "%" PRId64, i64FuncID);
        if (len < 0 || len >= static_cast<int>(sizeof(buf)))
        {
            return false;
        }
        return IsMatchInternal(buf, len);
    }

    /**
     * @brief 通用匹配接口,支持 uint64_t
     * @param u64FuncID 待匹配的數值
     * @return true 如果匹配成功,否則 false
     */
    bool IsMatch(const uint64_t u64FuncID) const
    {
        char buf[20]; // 最大 19位 + '\0'
        int len = snprintf(buf, sizeof(buf), "%" PRIu64, u64FuncID);
        if (len < 0 || len >= static_cast<int>(sizeof(buf)))
        {
            return false;
        }
        return IsMatchInternal(buf, len);
    }

    /**
     * @brief 通用匹配接口,支持 int32_t
     * @param i32FuncID 待匹配的數值
     * @return true 如果匹配成功,否則 false
     */
    bool IsMatch(const int32_t i32FuncID) const
    {
        char buf[12]; // 符號 + 10位 + '\0'
        int len = snprintf(buf, sizeof(buf), "%" PRId32, i32FuncID);
        if (len < 0 || len >= static_cast<int>(sizeof(buf)))
        {
            return false;
        }
        return IsMatchInternal(buf, len);
    }

    /**
     * @brief 通用匹配接口,支持 uint32_t
     * @param u32FuncID 待匹配的整數 ID
     * @return true 如果匹配成功,否則 false
     */
    bool IsMatch(const uint32_t u32FuncID) const
    {
        char buf[11]; // 10位 + '\0'
        int len = snprintf(buf, sizeof(buf), "%" PRIu32, u32FuncID);
        if (len < 0 || len >= static_cast<int>(sizeof(buf)))
        {
            return false;
        }
        return IsMatchInternal(buf, len);
    }

    /**
     * @brief 通用匹配接口,支持 std::string
     * @param strFuncID 待匹配的字符串
     * @return true 如果匹配成功,否則 false
     */
    bool IsMatch(const std::string &strFuncID) const
    {
        return IsMatchInternal(strFuncID.c_str(), strFuncID.size());
    }

    /**
     * @brief 通用匹配接口,支持 const char*
     * @param lpszFuncID 待匹配的 C 風格字符串
     * @return true 如果匹配成功,否則 false
     */
    bool IsMatch(const char *lpszFuncID) const
    {
        if (!lpszFuncID)
        {
            return false;
        }
        size_t len = strlen(lpszFuncID);
        return IsMatchInternal(lpszFuncID, len);
    }

    /**
     * @brief 獲取異常發生次數統計
     */
    inline uint32_t GetExceptionCount() const noexcept
    {
        return m_nExceptionCount;
    }

    /**
     * @brief 設置函數匹配模式。
     *
     * 默認行為是 **覆蓋原有模式**,除非通過 nFlag 指定追加模式。
     *
     * @param szlpFuncIDs 新的模式字符串,以逗號分隔
     * @param sep 分隔符, 默認逗號
     * @param nFlag 控制模式設置方式:
     *              - 0:覆蓋原有模式(默認)
     *              - APPEND_FUNCIDS_MODE:追加到現有模式
     * @return true 如果匹配成功,否則 false
     */
    bool SetFuncIDs(const char *szlpFuncIDs, char seq = ',', uint32_t nFlag = 0)
    {
        if (nFlag == 0) // 覆蓋模式,清空原有緩存
        {
            ClearAll();
        }

        try
        {
            return ParseFuncIDs(szlpFuncIDs, seq);
        }
        catch (...)
        {
            ClearAll(); // 異常則統統清空數據
            ++m_nExceptionCount;
            return false;
        }
    }

private: // 內部函數
    /**
     * @brief 清空所有內部緩存數據。
     */
    void ClearAll() noexcept
    {
        m_usExactMatchSet.clear();
        m_mapQuestionPatterns.clear();
        m_vsWildcardPatterns.clear();
    }

    /**
     * @brief 內部實現函數,執行實際匹配邏輯
     * @param lpszFuncID 待匹配字符串
     * @param nLen     字符串長度
     * @return true 如果匹配成功,否則 false
     */
    bool IsMatchInternal(const char *lpszFuncID, size_t nLen) const
    {
        try
        {
            // 1. 精確匹配集合
            if (m_usExactMatchSet.count(lpszFuncID))
            {
                return true;
            }

            // 2. 含 '?' 的匹配集合
            // 提前長度過濾
            auto it = m_mapQuestionPatterns.find(nLen);
            if (it != m_mapQuestionPatterns.end())
            {
                for (const auto &pattern : it->second)
                {
                    if (MatchQuestionOnly(pattern.c_str(), lpszFuncID, nLen))
                    {
                        return true;
                    }
                }
            }

            // 3. 含 '*' 的匹配集合
            for (const auto &pattern : m_vsWildcardPatterns)
            {
                if (WildcardMatch(pattern.c_str(), lpszFuncID))
                {
                    return true;
                }
            }

            return false;
        }
        catch (...)
        {
            // 記錄異常計數,但保持原有行為(返回 false)
            ++m_nExceptionCount;
            return false;
        }
    }

    /**
     * @brief 移除字符串中所有空白字符(包括空格、製表符、換行等)
     */
    static void NormalizeAndRemoveSpaces(std::string &s) noexcept
    {
        s.erase(std::remove_if(s.begin(), s.end(),
                               [](char c)
                               {
                                   return std::isspace(static_cast<unsigned char>(c));
                               }),
                s.end());
    }

    /**
     * @brief 解析輸入字符串並分類存儲模式。
     * @param szlpFuncIDs 輸入模式字符串,以逗號分隔
     * @param sep 分隔符
     * @return true 如果匹配成功,否則 false
     */
    bool ParseFuncIDs(const char *szlpFuncIDs, char seq)
    {
        if (!szlpFuncIDs)
        {
            return false;
        }

        std::string strFuncIDs(szlpFuncIDs);

        // 將整個輸入字符串去除所有空白
        NormalizeAndRemoveSpaces(strFuncIDs);

        size_t start = 0;
        while (start < strFuncIDs.size())
        {
            size_t pos = strFuncIDs.find(seq, start);
            std::string item = (pos == std::string::npos) ? strFuncIDs.substr(start) : strFuncIDs.substr(start, pos - start);
            start = (pos == std::string::npos) ? strFuncIDs.size() : pos + 1;

            // 輸入中連續逗號或空白導致的空項則跳過
            if (item.empty())
            {
                continue;
            }

            if (item.find('*') != std::string::npos)
            {
                m_vsWildcardPatterns.emplace_back(std::move(item));
            }
            else if (item.find('?') != std::string::npos)
            {
                m_mapQuestionPatterns[item.size()].emplace_back(std::move(item));
            }
            else
            {
                m_usExactMatchSet.insert(std::move(item));
            }
        }

        return true;
    }

    /**
     * @brief 匹配僅含 '?' 的模式。
     * @param szPattern 模式字符串
     * @param szStr     待匹配字符串
     * @param nLen      長度(已知相等)
     * @return true 若匹配成功
     */
    static bool MatchQuestionOnly(const char *pattern, const char *lpszStr, size_t len) noexcept
    {
        for (size_t i = 0; i < len; ++i)
        {
            if (pattern[i] != '?' && pattern[i] != lpszStr[i])
            {
                return false;
            }
        }
        return true;
    }

    /**
     * @brief 通配符匹配(支持 '*' 與 '?'),雙指針回溯匹配算法
     * @param szPattern 模式字符串
     * @param szStr     待匹配字符串
     * @return true 若匹配成功
     */
    static bool WildcardMatch(const char *lpszPattern, const char *lpszStr) noexcept
    {
        const char *pStar = nullptr;
        const char *sStar = nullptr;

        while (*lpszStr)
        {
            if (*lpszPattern == '?' || *lpszPattern == *lpszStr)
            {
                ++lpszPattern;
                ++lpszStr;
            }
            else if (*lpszPattern == '*')
            {
                while (*lpszPattern == '*') // 多個*則忽略
                {
                    ++lpszPattern;
                }

                if (*lpszPattern == '\0') // *可以不佔一位
                {
                    return true;
                }

                pStar = lpszPattern;
                sStar = lpszStr;
            }
            else if (pStar)
            {
                lpszPattern = pStar;
                lpszStr = ++sStar;
            }
            else
            {
                return false;
            }
        }

        while (*lpszPattern == '*')
        {
            ++lpszPattern;
        }

        return *lpszPattern == '\0';
    }

private:
    std::unordered_set<std::string> m_usExactMatchSet;                // 無通配符的精確匹配表
    std::map<size_t, std::vector<std::string>> m_mapQuestionPatterns; // 按長度分組的 '?' 模式
    std::vector<std::string> m_vsWildcardPatterns;                    // 含 '*' 的模式
    mutable uint32_t m_nExceptionCount;                               // 異常統計次數
};

int main()
{
    // 目標串
    const char *szlpFuncIDs = " 123?1 , 123456? ,123456* ,123843*3, 3819 , 3819?, 408?38*38 ";

    FuncMatcher *lpMatcher = new (std::nothrow) FuncMatcher();
    if (lpMatcher == nullptr)
    {
        std::cerr << "FuncMatcher init failed.\n";
        delete lpMatcher;
        return -1;
    }

    lpMatcher->SetFuncIDs(szlpFuncIDs);

    std::cout << std::boolalpha;

    // 測試數據,包含空格、製表符、換行等
    std::vector<std::pair<std::string, std::string>> vecTestData = {
        {"uint64_t", "408738464738"},
        {"uint64_t", "408738747373839"},
        {"uint32_t", "12341"},
        {"uint32_t", "1234567"},
        {"uint32_t", "55555"},
        {"uint32_t", "1238439934"},
        {"uint32_t", "3811"},
        {"uint32_t", "38192"},
        {"std::string", "123843993"},
        {"std::string", "99999"},
        {"const char*", "1234568"},
        {"const char*", "11111"},
        {"std::string", " 12341 "},     // 包含空格
        {"std::string", "\t1234567\n"}, // 包含製表符與換行
        {"std::string", " 38192 "}      // 包含空格
    };

    for (size_t i = 0; i < vecTestData.size(); ++i)
    {
        const auto &sType = vecTestData[i].first;
        const auto &sValue = vecTestData[i].second;
        bool bMatch = false;

        if (sType == "uint32_t")
        {
            uint32_t value = static_cast<uint32_t>(std::stoul(sValue));
            bMatch = lpMatcher->IsMatch(value);
        }
        else if (sType == "int32_t")
        {
            int32_t value = static_cast<int32_t>(std::stoi(sValue));
            bMatch = lpMatcher->IsMatch(value);
        }
        else if (sType == "uint64_t")
        {
            uint64_t value = static_cast<uint64_t>(std::stoull(sValue));
            bMatch = lpMatcher->IsMatch(value);
        }
        else if (sType == "int64_t")
        {
            int64_t value = static_cast<int64_t>(std::stoll(sValue));
            bMatch = lpMatcher->IsMatch(value);
        }
        else if (sType == "std::string")
        {
            bMatch = lpMatcher->IsMatch(sValue);
        }
        else if (sType == "const char*")
        {
            bMatch = lpMatcher->IsMatch(sValue.c_str());
        }

        std::cout << sType << " \"" << sValue << "\" -> " << bMatch << "\n";
    }

    // 輸出異常統計(可選)
    std::cout << "Exception count: " << lpMatcher->GetExceptionCount() << std::endl;

    delete lpMatcher;
    return 0;
}

輸出:

【C++11算法設計】模糊匹配算法_#c++


實現功能點

功能類別

功能點

説明

輸入類型支持

多類型匹配接口

支持 int32_tuint32_tint64_tuint64_tstd::stringconst char* 等類型輸入

整型自動轉換

整型輸入通過 snprintf 轉換為字符串再參與匹配,保證統一邏輯

字符串直接匹配

直接使用 std::string 或 C 風格字符串輸入進行匹配

通配符匹配規則

? 通配符

匹配任意單個字符(長度必須一致)

* 通配符

匹配任意長度字符(可為零長度)

混合通配符

支持 *? 混合使用,如 "408?38*38"

模式管理機制

模式分類存儲

精確匹配集合(無通配符)

? 模式集合(按長度分組)

* 模式集合(支持多層通配)

模式設置接口

SetFuncIDs():從字符串中解析多條規則(默認以逗號分隔)

模式追加功能

可通過標誌位 APPEND_FUNCIDS_MODE 選擇追加模式覆蓋模式

可自定義分隔符

用户可自定義除逗號外的其他單個字符的分隔符


總結

本文支持根據用户定義的通配規則,判斷輸入的數值或字符串是否與模式串精確匹配或通配匹配。