PHP 的 preg_match 配合 m (多行)、i (忽略大小寫)、s (點號匹配換行) 修飾符極其方便。初次用 C++ 寫爬蟲時,發現 libpcre 雖功能強大,但 API 為底層 C 風格,需手動管理編譯、執行、偏移、捕獲組,較易踩坑。本文記錄多行 HTML 內容提取的完整實踐。
pcre_exec 每次只匹配一次。要獲取所有匹配,必須在迴圈中:
pcre_exec,傳入當前偏移量 exec_offset。>=0),從 ovector 取得捕獲組起止位置。exec_offset = ovector[1] (第一個捕獲組結束位置) 繼續下一次匹配。PCRE_ERROR_NOMATCH (0) 或錯誤碼結束。#include <pcre.h>
#include <iostream>
#include <cstring>
#define OVECCOUNT 256
int main() {
// 目標文本:包含多個 href 的 HTML 片段
const char* pText = R"("21,537511285427,50005701,,shopsearch,1,shopcon,2950270077,," href="//detail.tmall.com/item.htm?id=537511285427&rn=54eb0efc1a7a49f5b93ed2051aa4fe9c&abbucket=0" target="_blank" data-gold-url="/inshopse" href="//detail.tmall.com/item.htm?id=537511285421&rn=54eb0efc1a7a49f5b93ed2051aa4fe9c&abbucket=0"href="//detail.tmall.com/item.htm?id=537511285422&rn=54eb0efc1a7a49f5b93ed2051aa4fe9c&abbucket=0"href="//detail.tmall.com/item.htm?id=537511285423&rn=54eb0efc1a7a49f5b93ed2051aa4fe9c&abbucket=0"href="//detail.tmall.com/item.htm?id=537511285424&rn=54eb0efc1a7a49f5b93ed2051aa4fe9c&abbucket=0" )";
// 模式:捕獲 //detail.tmall.com/item.htm?id=數字
const char* pPattern = "(//detail\\.tmall\\.com/item\\.htm\\?id=\\d+)";
const char* pErrMsg = nullptr;
int nOffset = 0;
// 編譯:PCRE_DOTALL 讓 . 匹配換行,PCRE_CASELESS 忽略大小寫
pcre* pPcre = pcre_compile(pPattern, PCRE_DOTALL | PCRE_CASELESS, &pErrMsg, &nOffset, nullptr);
if (!pPcre) {
std::cerr << "Regex compile error: " << pErrMsg << " at offset " << nOffset << std::endl;
return 1;
}
int ovector[OVECCOUNT];
int exec_offset = 0;
int count = 0;
do {
int rc = pcre_exec(pPcre, nullptr, pText, strlen(pText), exec_offset, 0, ovector, OVECCOUNT);
if (rc > 0) {
++count;
std::cout << "\nOK, matched #" << count << ":\n";
for (int i = 0; i < rc; ++i) {
const char* start = pText + ovector[2*i];
int len = ovector[2*i+1] - ovector[2*i];
std::cout << " Group $" << i << ": " << std::string(start, len) << std::endl;
}
exec_offset = ovector[1]; // 移動到第一個捕獲組結束處
} else if (rc == PCRE_ERROR_NOMATCH) {
break; // 正常結束
} else {
std::cerr << "Match error: " << rc << std::endl;
break;
}
} while (true);
std::cout << "\nTotal matches: " << count << std::endl;
pcre_free(pPcre);
return 0;
}
ovector[1] (第 0 組結束) 而非 ovector[0],避免死循環匹配同一位置。DOTALL 讓 . 匹配換行;MULTILINE 影響 ^$ 行首行尾行為。爬蟲常用 DOTALL。pcre_compile 分配的 pcre* 必須 pcre_free 釋放。Regex 類,內部管理 pcre*、ovector,提供 match_all 返回 vector<smatch>,隔離底層細節。掌握 libpcre 的編譯-執行-偏移循環模型,即可在 C++ 中實現 PHP 級別的靈活正則處理。配合 RAII 封裝,即能安全高效地應用於爬蟲、日誌分析等場景。