首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

C++ 使用 libpcre 實現多行正則匹配與捕獲

     編輯:關於C++

從 PHP 到 C++:正則表達式的跨語言挑戰

PHP 的 preg_match 配合 m (多行)、i (忽略大小寫)、s (點號匹配換行) 修飾符極其方便。初次用 C++ 寫爬蟲時,發現 libpcre 雖功能強大,但 API 為底層 C 風格,需手動管理編譯、執行、偏移、捕獲組,較易踩坑。本文記錄多行 HTML 內容提取的完整實踐。

核心流程:編譯 → 迴圈執行 → 偏移 → 捕獲

pcre_exec 每次只匹配一次。要獲取所有匹配,必須在迴圈中:

  1. 呼叫 pcre_exec,傳入當前偏移量 exec_offset。
  2. 若匹配成功 (>=0),從 ovector 取得捕獲組起止位置。
  3. 提取子串,處理業務邏輯。
  4. 更新 exec_offset = ovector[1] (第一個捕獲組結束位置) 繼續下一次匹配。
  5. 直到返回 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;
}

常見坑點與最佳實踐

  • OVECCOUNT:必須是 3 的倍數,且足夠大 (如 256) 以容納所有捕獲組。
  • 偏移更新:用 ovector[1] (第 0 組結束) 而非 ovector[0],避免死循環匹配同一位置。
  • PCRE_DOTALL vs PCRE_MULTILINE:DOTALL 讓 . 匹配換行;MULTILINE 影響 ^$ 行首行尾行為。爬蟲常用 DOTALL。
  • 記憶體管理:pcre_compile 分配的 pcre* 必須 pcre_free 釋放。
  • C++ 封裝建議:生產環境建議封裝 Regex 類,內部管理 pcre*、ovector,提供 match_all 返回 vector<smatch>,隔離底層細節。

結語

掌握 libpcre 的編譯-執行-偏移循環模型,即可在 C++ 中實現 PHP 級別的靈活正則處理。配合 RAII 封裝,即能安全高效地應用於爬蟲、日誌分析等場景。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved