轉自個人博客
1. 概述
1.1 正則表達式概述
正則表達式(Regular Expressions,簡稱 regex)是用于匹配文本模式的一種特殊字符序列,其可以用一系列字符來表示出不同文本的對應模式。正則表達式的應用范圍十分廣泛,包括驗證文本格式、判斷字符種類、解析文本信息、轉換目標文本、遍歷搜索文本、符號化文本等。
一般在文件搜索、瀏覽器搜索時都可以使用正則表達式來表達某一種想要的文本格式,在C++等編程語言中也是一樣,靈活正當地使用正則表達式可以很有效地提高代碼的可讀性和簡潔性。
另外要注意的是,隨著時間的推移,正則表達式的語法也在擴充升級,而C++對以下幾種語法均有著很好的支持,其中最主要的掌握ECMAScript就好:
- ECMAScript:基于ECMAScript標準的語法。JavaScript、ActionScript和Jscript等語言都使用該標準。
- basic:基本的POSIX語法。
- extended:拓展的POSIX語法。
- awk:POSIX awk實用工具使用的語法。
- grep:POSIX grep實用工具使用的語法。
- egrep:用逗號分隔的POSIX egrep語法。
1.2 C++中使用正則表達式
在 C++11 之前,C++ 并未內置正則表達式庫,不能直接使用正則表達式。如果開發者想要使用正則表達式對文本作快捷操作,那么就需要依賴第三方庫如 PCRE (Perl Compatible Regular Expressions) 或使用 Boost庫中的 Boost.Regex。而在 C++11 中,C++ 標準庫引入了<regex>
頭文件,這樣在C++中使用正則表達式就不需要鏈接導入第三方庫了。
示例:
#include <iostream>
#include <regex>
using namespace std;int main(){string text = "The number is 42";regex number_pattern("\\d+"); // 建立匹配模式,查找字符串中是否有數字if(regex_search(text, number_pattern)){cout << "Found a number!" << endl;}
}
2. 正則表達式的語法規則
正則表達式會用到的特殊字符為:^ $ \ . * + ? () [] {} |
每個字符的含義和用處將在下面逐步介紹。
2.1 單字符匹配
除了正則表達式會用到的特殊字符外,單個字符(包括大小寫字母、數字、其他字符等)就匹配指定的字符,比如:a
匹配字符 a
。
如果要匹配特殊字符,則需要使用特殊字符中的轉義字符\
,比如:\^
匹配字符 ^
對于需要匹配的文本中有未指定字符,就可以使用通配符.
(點,英文句號)。通配符可以匹配任意單個字符,一般用在有部分不確定字符的情況下,比如a.c
可以匹配到abc
、a1c
等一系列文本。只使用單個通配符就表示只匹配總長度為1的文本。
如果指定匹配任意數字或任意字母等非全部的情況時,可以使用字符類來指定一個范圍的字符,見下一小節。
2.2 字符類
-
預定義字符類:
是正則表達式內定的用于表示一類字符的情況,用于匹配單一字符
\\d
:匹配任何數字字符(0-9)。\\D
:匹配任何非數字字符。\\w
:匹配任何字母、數字或下劃線(A-Z、a-z、0-9 和_
)。\\W
:匹配任何非字母、數字或下劃線的字符。\\s
:匹配任意空白字符(如空格、制表符、換行符等)。\\S
:匹配任何非空白字符。
-
自定義字符集:
正則表達式支持使用
[]
來自定義字符集,用于匹配單一字符,比如:[abc]
匹配字符a
、b
或c
。使用**
-
**來指定字典順序的前后范圍,比如:[0-9]
匹配任意數字;[a-z]
匹配任意小寫字母;[A-Za-z]
匹配任意字母 -
反向字符類(排除指定字符)
在字符類
[]
中使用^
作為第一個字符(緊跟‘[’之后才會生效),表示匹配不在該集合中的字符。示例如下[^a-z]
:匹配非小寫字母的任意字符[^0-9]
:匹配非數字的任意字符[^aeiou]
:匹配非元音字母的任意字符
// 組合使用示例:匹配不含字母但必須包含數字的字符串 std::regex no_letters_but_digits(R"(^[^A-Za-z]*[0-9]+[^A-Za-z]*$)");
2.3 重復字符匹配
在正則表達式中,特殊字符*
、+
、?
被稱為量詞,用于匹配某個字符是否多次重復出現的情況。
-
*
(星號):表示出現零次或多次。用于匹配此符號前一個字符零次或多次,比如:
a*
可以匹配到空字符串
、a
、aa
、aaa
等。 -
+
(加號):表示出現一次或多次。用于匹配此符號前一個字符一次或多次,比如:a+
可以匹配到
a、
aa、
aaa` 等,但不會匹配空字符串。 -
?
(問號):表示出現零次或一次。用于匹配此符號前一個字符零次或一次,比如:
a?
可以匹配到空字符串
或者a
。
除此之外,還可以使用**{}
**來指定出現次數的方位,格式為{min, max}
,即匹配此符號前一個字符至少min
次,至多max
次,可省略其中的,
和后一個max
數值:
-
{n}
:用于匹配此符號前一個字符正好n次,比如:
a{3}
只能匹配到aaa
。 -
{min, }
:用于匹配此符號前一個字符至少min次,比如:
a{3, }
可以匹配到aaa
、aaaa
、aaaaa
等。 -
{min, max}
:用于匹配此符號前一個字符至少min次,至多max次,比如:
a{3, 5}
只能匹配到aaa
、aaaa
、aaaaa
。
2.4 邊界匹配
在正則表達式中,可以單獨指定匹配文本的開頭或結尾。分別使用**錨點^
來指定開頭,使用錨點$
**來指定結尾
-
^
:將這個符號放在文本首位,表明文本在此開頭。用于匹配字符串的開頭,比如:正則表達式
^abc
匹配以abc開頭的任意文本abc1
、abcd
等 -
$
:將這個符號放在文本末尾,表明文本在此結尾。用于匹配字符串的結尾,比如:正則表達式
abc$
匹配以abc結尾的任意文本1abc
、dabc
等
錨點還可以混合使用,同時指定文本的開頭和結尾,比如:^abc$
將只匹配字符串abc
。還可以更復雜的混合使用,比如:^abc.*123$
就可以匹配以abc開頭和123結尾的任意文本了。
錨點除了這兩個最常用的,還有很多指定更多特殊情況的,比如以下四種:
-
\b
:用于匹配文本中指定的字母字符串(單詞)有邊界的情況。有
\b
代表必須有邊界,邊界是指無任意字符,當然可以是空格。比如:正則表達式
\babc
會匹配abc前面有邊界的情況,如xyz abc
(abc前面有空格作邊界,無其他字符相連)、abcdef
(abc前面無其他字符相連,后面不用管),而不能匹配abclabc
;正則表達式\babc\b
會匹配abc前后有邊界的情況,如abc
、xyz abc nnn
,但不會匹配111abc
或abcddd
. -
\B
:與
\b
的情況相反,用于匹配文本中指定的字母字符串(單詞)無邊界的情況。比如:正則表達式\Babc
會匹配xabc
(abc前面無邊界) ,但不會匹配abc
。 -
\A
:用于匹配字符串的絕對開頭,與
^
的行為相似,但是^
在多行模式下可能匹配每行的開頭,而\A
始終匹配整個字符串的開頭。比如:\Aabc
只會匹配以 “abc” 開頭的字符串。 -
\Z
:用于匹配字符串的絕對結尾,與
$
的行為相似,但是$
在多行模式下可能匹配每行的結尾,而\Z
始終匹配整個字符串的結尾。比如:\Z123
只會匹配以123
結尾的字符串。
2.5 分組和選擇
正則表達式通過 ()
可以將多個字符組合為一個組,可以對一個組的字符進行統一操作,比如:(ab)+
可以匹配 ab
、abab
、ababab
等。
正則表達式使用 |
表示選擇,可以理解為邏輯中的或,比如: (a|b)
表示匹配字符 a
或 b
。
3. C++<regex>
標準庫的基本使用
<regex>
類是C++11引入的標準庫,需要包含頭文件#include <regex>
,并使用命名空間std
。
官方中文文檔點這里傳送。
3.1 常用類和方法
std::regex
: 表示一個正則表達式。它提供了存儲和操作正則表達式的能力。std::smatch
: 用于存儲std::regex_match
函數的結果。這是一個用于匹配字符串的結果類型,通常使用于std::string
。std::cmatch
: 與std::smatch
類似,但用于處理 C 風格字符串(C-strings)。std::regex_search
: 用于在字符串中搜索與正則表達式匹配的部分。std::regex_match
: 用于檢查一個字符串是否完全匹配給定的正則表達式。std::regex_replace
: 用于替換字符串中與正則表達式匹配的部分。
3.2 創建正則表達式
使用std::regex
類來聲明一個正則表達式的匹配模式
示例:
/// 聲明一個正則表達式,用于之后匹配一個或多個小寫字母
std::regex pattern("[a-z]+");
/// 只禁止輸入_,其他字符不受影響
std::regex no_underscore(R"(^[^_]*$)");
注意:C++中可以通過對字符串使用**R"( )"
**修飾來告訴編譯器保持字符串原樣,不處理轉義字符。前面說過,使用特殊字符需要使用\
進行雙重轉義。
而這一般會用在正則表達式、文件路徑、JSON/XML數據等,普通字符串一般沒必要使用。
std::regex re("\\d+"); // 普通字符串:雙重轉義后,實際傳遞的正則是 "\d+"
std::regex re(R"(\d+)"); // 原始字符串:直接傳遞正則 "\d+"
3.3 匹配
使用**std::regex_match()
來檢查整個字符串**是否完全匹配正則表達式。
第一個參數為目標字符串,第二個參數為指定正則表達式。匹配返回true,否則返回false。
示例:
std::string str = "hello";
std::regex pattern("hello"); if (std::regex_match(str, pattern)) { std::cout << "完整匹配成功!" << std::endl;
} else { std::cout << "不匹配。" << std::endl;
}
使用**std::regex_search()
**查找字符串中是否存在與模式匹配的子字符串。
第一個參數為目標字符串,第二個參數為指定正則表達式。匹配返回true,否則返回false。
std::string str = "hello world";
std::regex pattern("world"); if (std::regex_search(str, pattern)) { std::cout << "找到匹配的部分!" << std::endl;
} else { std::cout << "未找到匹配。" << std::endl;
}
3.4 捕獲組
在正則表達式中用括號()
包圍一段模式來捕獲文本中符合格式的子字符串,被稱為捕獲組。當正則表達式包含捕獲組時,可以通過 std::smatch
或 std::cmatch
來保存匹配的結果。且要注意在聲明正則表達式時使用**關鍵字R
**來激活捕獲組。
std::smatch
或 std::cmatch
對象放在std::regex_match()
第二個參數。
示例:
std::string str = "2023-10-08";
std::regex pattern(R"(\d{4})-(\d{2})-(\d{2})"); // 分別捕獲年、月、日格式 /// 創建 smatch 對象以保存匹配結果
std::smatch match;
if (std::regex_match(str, match, pattern)) { std::cout << "年: " << match[1] << ", 月: " << match[2] << ", 日: " << match[3] << std::endl;
}
3.5 替換
使用 std::regex_replace()
替換匹配部分。
第一個參數為目標字符串,第二個參數為指定正則表達式,第三個參數為替換的字符串。替換成功后返回替換后的字符串,否則返回原字符串。
示例:
std::string str = "I love cats and cats are great.";
std::regex pattern("cats");
std::string replaced = std::regex_replace(str, pattern, "dogs"); std::cout << "替換后的字符串: " << replaced << std::endl;
3.6 指定正則選項
<regex>
允許在創建 std::regex
對象時,通過第二個參數指定一些正則選項,使用std::regex_constants
命名空間中的常量來指定選項。
-
std::regex_constants::icase
:忽略大小寫在匹配時,忽略字符的大小寫。
-
std::regex_constants::multiline
:多行匹配在多行字符串中,
^
和$
匹配每行的開始和結束,而不僅僅是整段字符串的開始和結束。 -
std::regex_constants::dotall
:點號匹配換行符.
匹配的任意字符中增加換行符。 -
std::regex_constants::ECMAScript
:使用 ECMAScript 語法 -
std::regex_constants::basic
:使用基本的 POSIX 語法 -
std::regex_constants::extended
:使用擴展的 POSIX 語法
注意:可以使用|
來組合多個選項
示例
/// 只禁止輸入_,其他的可以輸入
std::regex no_underscore(R"(^[^_]*$)");/// 忽略大小寫
std::regex pattern("hello", std::regex_constants::icase); /// 使用基本的 POSIX 語法
std::regex pattern("hello\\s\\(world\\)", std::regex_constants::basic);/// 忽略大小寫并且支持多行匹配
std::regex pattern("hello", std::regex_constants::icase | std::regex_constants::multiline);