| 類別 | 功能 | 關鍵方法 |
rpattern | 正則模式編譯、旗標設定 | match(), substitute(), split(), cgroups() |
match_results | 匹配結果容器 | all_backrefs(), backref(i), cbackrefs(), matched |
subst_results | 替換結果 | 輸出字串、替換次數 |
split_results | 分割結果 | operator[], size() |
常用旗標
GLOBAL:全域匹配(非首次)
ALLBACKREFS:捕獲所有反向引用
NOCASE:忽略大小寫
MULTILINE:多行模式 ^$ 匹配行首尾
SINGLELINE:單行模式 . 匹配換行
完整測試程式:MFC 對話框應用
介面配置
- 編輯框:
m_strReg(正則)、m_strSource(源文本)、m_strSub(替換文本)、m_strResult(結果)
- 核取方塊:
m_bCase(忽略大小寫)、m_bMulti(多行)、m_bSingle(單行)
- 按鍵觸發:F5=匹配、F6=替換、F7=分割
核心實現代碼
void CRegTestDlg::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
if (nChar == VK_ESCAPE) { CDialog::OnOK(); return; }
UpdateData(TRUE);
// 旗標組合
REGEX_FLAGS dw = GLOBAL | ALLBACKREFS;
if (m_bCase) dw |= NOCASE;
if (m_bMulti) dw |= MULTILINE;
if (m_bSingle) dw |= SINGLELINE;
rpattern pat((LPCTSTR)m_strReg, dw);
int nGroups = pat.cgroups(); // 捕獲組數
if (nChar == VK_F5) { // === 匹配查找 ===
m_strResult = _T("");
match_results result;
double tmS = clock();
match_results::backref_type br = pat.match((LPCTSTR)m_strSource, result);
int nCount = 0;
if (br.matched) {
// 遍歷方式 1:all_backrefs() 向量
match_results::backref_vector vec = result.all_backrefs();
for (auto it = vec.begin(); it != vec.end(); ++it) {
nCount++;
m_strResult += it->str().c_str();
m_strResult += _T("\r\n---------------------------------------------\r\n");
}
// 遍歷方式 2:索引訪問(按組分組)
for (int i = 0; i < result.cbackrefs(); i++) {
if (i % nGroups == 0) { // 每組第一個為整體匹配
nCount++;
m_strResult += result.backref(i).str().c_str();
m_strResult += _T("\r\n---------------------------------------------\r\n");
}
}
}
double tmE = clock();
CString tip; tip.Format(_T("耗時 %.2f ms, 匹配 %d 處"), tmE - tmS, nCount);
GetDlgItem(IDC_STATIC_TIP)->SetWindowText(tip);
UpdateData(FALSE);
}
else if (nChar == VK_F6) { // === 替換 ===
m_strResult = _T("");
double tmS = clock();
rpattern pat2((LPCTSTR)m_strReg, (LPCTSTR)m_strSub, dw);
subst_results subResult;
string str((LPCTSTR)m_strSource);
int nCount = pat2.substitute(str, subResult);
m_strResult = str.c_str();
double tmE = clock();
CString tip; tip.Format(_T("耗時 %.2f ms, 替換 %d 處"), tmE - tmS, nCount);
GetDlgItem(IDC_STATIC_TIP)->SetWindowText(tip);
UpdateData(FALSE);
}
else if (nChar == VK_F7) { // === 分割 ===
m_strResult = _T("");
double tmS = clock();
rpattern pat3((LPCTSTR)m_strReg, dw);
split_results splitResult;
string str((LPCTSTR)m_strSource);
int nCount = pat3.split(str, splitResult);
for (int i = 0; i < nCount; i++) {
m_strResult += splitResult[i].c_str();
m_strResult += _T("\r\n---------------------------------------------\r\n");
}
double tmE = clock();
CString tip; tip.Format(_T("耗時 %.2f ms, 分割 %d 段"), tmE - tmS, nCount);
GetDlgItem(IDC_STATIC_TIP)->SetWindowText(tip);
UpdateData(FALSE);
}
CDialog::OnKeyDown(nChar, nRepCnt, nFlags);
}

Greta 測試工具運行截圖
常用正則範例速查
| 需求 | 正則 | 說明 |
| Email | \w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)* | 標準郵箱格式 |
| 手機號(大陸) | 1[3-9]\d{9} | 11位、1開頭 |
| IPv4 | ((25[0-5]|2[0-4]\d|[01]?\d?\d)\.){3}(25[0-5]|2[0-4]\d|[01]?\d?\d) | 四段 0-255 |
| 中文 | [\u4e00-\u9fa5]+ | Unicode 範圍 |
| HTML標籤 | <[^>]+> | 貪婪匹配 |
| 日期 YYYY-MM-DD | \d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]) | 簡易驗證 |
VC6 常見編譯錯誤與對策
| 錯誤現象 | 原因 | 解決 |
| C1010: unexpected end of file while looking for precompiled header | .cpp 未含 stdafx.h | 在 regexpr.cpp/regexpr2.cpp 首行加 #include "stdafx.h" |
| C2xxx: 樣板/命名空間相關錯誤 | VC6 樣板支援不全 | 設定 MFC 靜態連結,或升級 VC6 SP6 |
| LNK2001: 未解析外部符號 | .cpp 未加入專案 | 專案右鍵 Add Files 選中 regexpr.cpp regexpr2.cpp |
| 運行時 Access Violation | 正則語法錯誤導致內部崩潰 | 用 try-catch 包裹 pat.match(),輸入前驗證 |
效能與替代方案
- Greta 基於回溯 NFA,極端情況(如
(a+)+b 匹配 aaaaac)可能指數級回溯
- 高效能場景建議:RE2(Google,DFA保證線性時間)、PCRE2(JIT編譯)、std::regex(C++11,VC10+)
- 輕量嵌入式:
tiny-regex-c(單檔、無依賴、功能受限)
總結
Greta 以 極小體積、Perl相容語法、STL風格介面 成為 VC6 專案引入正則的最佳選擇。本文提供的測試工具可作為專案整合驗收標準,三大核心功能(匹配/替換/分割)代碼可直接遷移至實際業務模組。建議配合《正則表達式30分鐘入門》快速掌握語法,實戰中善用 GLOBAL|ALLBACKREFS 旗標獲取完整捕獲組資訊。
- 上一篇文章: MFC WinSock ActiveX控件開發實戰(一):從專案建立到基礎連線事件實現
- 下一篇文章: VC++嵌入式數據庫SQLite完整應用指南:從建庫到C API實戰
Copyright ©
程式碼之家 All Rights Reserved