天天看點

C++ 正規表達式不支援零寬斷言 lookbehind

正規表達式零寬斷言

适用場景:查找/替換以 xxx 開頭,或以 xxx 結尾,但不包括 xxx 的字元串。

零寬斷言 用法 含義

​(?=exp)​

​ 零寬度正預測先行斷言

​exp1(?=exp2)​

​exp1​

​ 之後必須比對 ​

​exp2​

​,但比對結果不含 ​

​exp2​

​(?!exp)​

​ 零寬度負預測先行斷言

​exp1(?!exp2)​

​exp1​

​ 之後必須不比對 ​

​exp2​

​(?<=exp)​

​ 零寬度正回顧後發斷言

​(?<=exp0)exp1​

​exp1​

​ 之前必須比對 ​

​exp0​

​exp0​

​(?<!exp)​

​ 零寬度負回顧後發斷言

​(?<!exp0)exp1​

​exp1​

​ 之前必須不比對 ​

​exp0​

示例:提取​

​【123】​

​中的 ​

​123​

​:​

​(?<=【)\d+(?=】)​

問題描述

正規表達式比對形似 ​

​qq=123456​

​ 的字元串,從中提取 ​

​123456​

​,但不包括 ​

​qq=​

​。首先想到的是直接利用零寬斷言 lookbehind 去比對,正規表達式很容易寫出來 ​

​(?<=qq=)[0-9]+​

​,但是在 C++ 運作階段報錯:

terminate called after throwing an instance of 'std::regex_error'
  what():  Invalid special open parenthesis.
Aborted (core dumped)
      

問題分析

目前 C++ 标準庫正規表達式不支援零寬後行斷言(也叫零寬度正回顧後發斷言,lookbehind),即 ​

​(?<=exp)​

​ 和 ​

​(?<!exp)​

​ 文法。但支援零寬前行斷言(lookahead)。

Finally, flavors like std::regex and Tcl do not support lookbehind at all, even though they do support lookahead. JavaScript was like that for the longest time since its inception. But now lookbehind is part of the ECMAScript 2018 specification. As of this writing (late 2019), Google’s Chrome browser is the only popular JavaScript implementation that supports lookbehind. So if cross-browser compatibility matters, you can’t use lookbehind in JavaScript.

解決方案

  1. 構造 regex 時指定可選标志,使用其他正規表達式文法 ==> 驗證失敗 ????
  2. 把待提取部分用()括起來,作為一個獨立子表達式 ==> 驗證可行 ????

示例代碼

#include <iostream>
#include <regex>
#include <string>

using namespace std;
using namespace regex_constants;

int main()
{
string seq = "[optional]qq=123456;";
// string pattern = "(?<=qq=)[0-9]+"; // C++ 正規表達式不支援 lookbehind,運作時報錯
string pattern = "qq=([0-9]+)"; // 将數字部分單獨作為一個子表達式
regex r(pattern /*, extended*/); // 可以在這裡修改預設正規表達式文法,然而并沒有什麼用
smatch results;

if (regex_search(seq, results, r)) {
cout << "regex_search --> true; results.size() --> " << results.size() << endl;
cout << results[0] << endl; // 列印整個比對
cout << results[1] << endl; // 列印第一個正則子表達式
cout << results[2] << endl;
   } else {
cout << "regex_search --> false" << endl;
   }

if (regex_match(seq, results, r)) {
cout << "regex_match --> true; results.size() --> " << results.size() << endl;
cout << results[0] << endl; // 列印整個比對
cout << results[1] << endl; // 列印第一個正則子表達式
cout << results[2] << endl;
   } else {
cout << "regex_match --> false" << endl; // seq 中的“[optional]”和“;”與 pattern 不比對
   }
}
      

輸出結果

$ g++ regex.cpp && ./a.out
regex_search --> true; results.size() --> 2
qq=123456
123456

regex_match --> false
      

補充

​regex_match(seq[, match], exp[, matchFlag])​

​ 整個 seq 與 exp 比對時傳回 true。

​regex_search(seq[, match], exp[, matchFlag])​

​ 找到一個比對字串就停止查找,傳回 true。

​match.size()​

​ 比對失敗傳回 0,否則傳回子表達式數量。

​match.str(n)​

​ 與第 n 個子表達式比對的 ​

​string​

​。n 預設為 0,且小于 ​

​match.size()​

​match[n]​

​ 對應第 n 個子表達式的 ​

​ssub_match​

​ 對象。n 預設為 0,且小于 ​

​match.size()​

C++ regex 預設采用 ECMAScript 文法。功能強大,使用廣泛,建議使用預設 ECMAScript。

C++ 正規表達式的文法在運作時解析、編譯,非常慢,避免建立不必要的正規表達式。特别是在循環中。

可以通過 ​

​R"(...)"​

​ 來忽略 C++ 中的轉義字元 ​

​\​

​,如 ​

​regex r(R"(\d+)");​

繼續閱讀