前言
在實際應用中,系統經常需要對一組標識符進行快速匹配。用户可能希望通過通配符模式靈活指定匹配規則,而不是手動列出每一個具體 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;
}
輸出:
實現功能點
|
功能類別
|
功能點
|
説明
|
|
輸入類型支持 |
多類型匹配接口
|
支持 |
|
整型自動轉換
|
整型輸入通過 |
|
|
字符串直接匹配
|
直接使用 |
|
|
通配符匹配規則 |
|
匹配任意單個字符(長度必須一致)
|
|
|
匹配任意長度字符(可為零長度)
|
|
|
混合通配符
|
支持 |
|
|
模式管理機制 |
模式分類存儲
|
精確匹配集合(無通配符)
|
|
模式設置接口
|
|
|
|
模式追加功能
|
可通過標誌位 |
|
|
可自定義分隔符
|
用户可自定義除逗號外的其他單個字符的分隔符
|
總結
本文支持根據用户定義的通配規則,判斷輸入的數值或字符串是否與模式串精確匹配或通配匹配。