0%

C++11新特性 - 正则表达式

C++11 新增了正则表达式的标准库支持,本文简介 C++ 正则表达式的使用

在 C++ 中使用正则表达式,和其它语言差别不大

1
2
3
4
5
6
7
int main() {
regex e("abc*");
bool match = regex_search("abccc", e);

// 输出 Matched
cout << (match ? "Matched" : "Not matched") << endl;
}

C++11 自带了 6 种正则表达式语法的支持

  1. ECMAScript
  2. basic
  3. extended
  4. awk
  5. grep
  6. egrep

C++11 默认使用 ECMAScript 语法,这也是 6 种语法中最强大的,假如想使用其他 5 种语法,只需在声明 regex 对象时指定即可

1
regex e("^abc.", regex_constants::grep);

假如我们不仅仅想知道一个正则表达式是否匹配一个字符串,我们还想要提取出匹配的部分,例如我们需要从邮箱中提取用户名和网址,就需要用到 match_results

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int main() {
string str("Email a@bc.com is mine");
smatch m; //等同于 match_results<string>

regex e("([[:w:]]+)@([[:w:]]+\\.com)");
bool found = regex_search(str, m, e);

// m.size=3, 存储了 3 个 result
cout << "m.size=" << m.size() << endl;

/* 迭代 match_results, 输出
m[0]=a@bc.com,整个匹配
m[1]=a,第1个group
m[2]=bc.com,第2个group
*/
for (int n = 0; n < m.size(); n++) {
cout << "m[" << n << "]=" << m[n].str() << endl;
// 等价写法 m.str(n), *(m.begin()+n)
}
// m.prefix=Email
cout << "m.prefix=" << m.prefix().str() << endl;
// m.suffix= is mine
cout << "m.suffix=" << m.suffix().str() << endl;
}

假如我们想要匹配的字符串中,有多个子串都可以匹配正则表达式,并且我们想把这些子串全部找出来,例如一个字符串中包含多个邮箱地址,那么就需要用到 regex_iterator

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int main() {
string str("a@bc.com, d@ef.com, aa@bb.com");

regex e("([[:w:]]+)@([[:w:]]+\\.com)");

sregex_iterator pos(str.cbegin(), str.cend(), e); // 定义 regex_iteraror
sregex_iterator end; // C++惯例: 默认构造的迭代器表示序列结束

/*
email=a@bc.com, user=a, domain=bc.com
email=d@ef.com, user=d, domain=ef.com
email=aa@bb.com, user=aa, domain=bb.com
*/
for (; pos!=end; pos++) {
cout << "email=" << pos->str(0) << ", user=" << pos->str(1) << ", domain=" << pos->str(2) << endl;
}
}

如上我们可以看到,regex_iterator 其实就是迭代字符串中所有正则表达式匹配的 match_results

除此之外,C++ 还提供了另一种迭代器, regex_token_iterator。不同的是,regex_token_iterator 迭代的是所有正则表达式匹配中的指定子表达式,或迭代未匹配的子字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int main() {
string str("a@bc.com, d@ef.com, aa@bb.com");

regex e("([[:w:]]+)@([[:w:]]+\\.com)");

sregex_token_iterator pos(str.cbegin(), str.cend(), e); // 定义regex_token_iterator
sregex_token_iterator end; // 序列结束

/*
Matched: a@bc.com
Matched: d@ef.com
Matched: aa@bb.com
*/
for (; pos!=end; pos++) {
cout << "Matched: " << *pos << endl;
}
}

我们可以修改 pos 的定义,使它每次迭代 match_results 的第 2 个 group

1
2
// 第 4 个参数表示第几个 group
sregex_token_iterator pos(str.cbegin(), str.cend(), e, 2);

值得注意的是,如果我们把这里的参数设为 -1,则迭代字符串中所有不匹配正则表达式的部分,相当于用正则表达式切割字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int main() {
string str("a bb cd");

regex e("\\s+"); // 匹配空格

// 迭代不匹配正则表达式的部分
sregex_token_iterator pos(str.cbegin(), str.cend(), e, -1);
sregex_token_iterator end;

/*
Matched: a
Matched: bb
Matched: cd
*/
for (; pos!=end; pos++) {
cout << "Matched: " << *pos << endl;
}
}

正则表达式还有一个常用的场景——字符串替换。C++ 中我们可以使用 regex_replace

1
2
3
4
5
6
7
8
int main() {
string str("a@bc.com, d@ef.com, aa@bb.com");

regex e("([[:w:]]+)@([[:w:]]+\\.com)");

// a is on bc.com, d is on ef.com, aa is on bb.com
cout << regex_replace(str, e, "$1 is on $2");
}