前言
在实际应用中,系统经常需要对一组标识符进行快速匹配。用户可能希望通过通配符模式灵活指定匹配规则,而不是手动列出每一个具体 ID。
其中通配符解释如下:
通配符 含义 示例 可匹配的字符串 ?匹配任意单个字符 AB?DAB1D,ABXD*匹配任意长度(0~N)的任意字符 12*891289,123456789,12ABCD89场景示例:
用户希望匹配函数 ID:
12341、12342、12343…手动列出所有 ID 很繁琐
希望使用通配符:
1234?或1234* 或 123?4*9等自动匹配符合规则的 ID因此,需要一个 高效、灵活、安全的通配符匹配算法,既支持整数也支持字符串。
算法设计
整体设计思路:
[用户输入模式串]
│
▼
[去除空白字符]
│
▼
[指定分隔符(默认逗号)拆分模式串]
│
▼
[分类存储模式]
┌───────────────┐
│精确匹配集合 │
│? 模式集合 │
│* 模式集合 │
└───────────────┘
│
▼
[用户输入待匹配值] ──► 整数? ──► 转字符串
│
▼
[匹配顺序]
┌─────┬───────┬─────┐
│精确匹配│? 模式 │* 模式│
└─────┴───────┴─────┘
│
▼
匹配成功? → true
否 → 返回 false
说明:
模式预处理(SetFuncIDs / ParseFuncIDs)
将输入的模式字符串拆分成若干条规则(支持指定,默认逗号分隔)
去除空格、制表符等空白字符(NormalizeAndRemoveSpaces)
将模式按类型分类存储:
精确匹配集合(
m_usExactMatchSet):没有通配符的字符串
?模式集合(m_mapQuestionPatterns):按长度分组存储,便于快速筛选
*模式集合(m_vsWildcardPatterns):存储带*的复杂模式匹配流程(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_t、uint32_t、int64_t、uint64_t、std::string、const char* 等类型输入 |
| 整型自动转换 | 整型输入通过 snprintf 转换为字符串再参与匹配,保证统一逻辑 | |
| 字符串直接匹配 | 直接使用 std::string 或 C 风格字符串输入进行匹配 | |
| 通配符匹配规则 | ? 通配符 | 匹配任意单个字符(长度必须一致) |
* 通配符 | 匹配任意长度字符(可为零长度) | |
| 混合通配符 | 支持 * 与 ? 混合使用,如 "408?38*38" | |
| 模式管理机制 | 模式分类存储 |
精确匹配集合(无通配符)
|
| 模式设置接口 | SetFuncIDs():从字符串中解析多条规则(默认以逗号分隔) | |
| 模式追加功能 | 可通过标志位 APPEND_FUNCIDS_MODE 选择追加模式或覆盖模式 | |
| 可自定义分隔符 | 用户可自定义除逗号外的其他单个字符的分隔符 |
总结
本文支持根据用户定义的通配规则,判断输入的数值或字符串是否与模式串精确匹配或通配匹配。
442

被折叠的 条评论
为什么被折叠?



