【C++11算法设计】模糊匹配算法

前言

在实际应用中,系统经常需要对一组标识符进行快速匹配。用户可能希望通过通配符模式灵活指定匹配规则,而不是手动列出每一个具体 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):存储带 * 的复杂模式

  2. 匹配流程(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;
}

输出:


实现功能点

功能类别功能点说明
输入类型支持多类型匹配接口支持 int32_tuint32_tint64_tuint64_tstd::stringconst char* 等类型输入
整型自动转换整型输入通过 snprintf 转换为字符串再参与匹配,保证统一逻辑
字符串直接匹配直接使用 std::string 或 C 风格字符串输入进行匹配
通配符匹配规则? 通配符匹配任意单个字符(长度必须一致)
* 通配符匹配任意长度字符(可为零长度)
混合通配符支持 *? 混合使用,如 "408?38*38"
模式管理机制模式分类存储

精确匹配集合(无通配符)

? 模式集合(按长度分组)

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

模式设置接口SetFuncIDs():从字符串中解析多条规则(默认以逗号分隔)
模式追加功能可通过标志位 APPEND_FUNCIDS_MODE 选择追加模式覆盖模式
可自定义分隔符用户可自定义除逗号外的其他单个字符的分隔符

总结

本文支持根据用户定义的通配规则,判断输入的数值或字符串是否与模式串精确匹配或通配匹配。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SarPro

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值