正则表达式
正则表达式
正则表达式描述了一种字符串匹配的模式。一般使用正则表达式主要是实现下面三个需求:
- 检查一个串是否包含某种形式的子串;
- 将匹配的子串替换;
- 从某个串中取出符合条件的子串。
正则表达式在编译原理中已经学过了,本质上就是建立状态机进行文本匹配。而我们通过一定的算法将状态机转换为一个精简的字符表达式,这个表达式就是正则表达式。大多数语言都支持正则表达式,因为我主要使用C++,所以就用C++的
组成
正则表达式由普通字符和特殊字符组成组成。
普通字符
普通字符包括没有显式指定为元字符的所有可打印和不可打印字符。这包括所有大写和小写字母、所有数字、所有标点符号和一些其他符号。
特殊字符
特殊字符是正则表达式里有特殊含义的字符,也是正则表达式的核心匹配语法。参见下表:
特别字符 | 描述 |
---|---|
$ | 匹配输入字符串的结尾位置。 |
(,) | 标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。 |
* | 匹配前面的子表达式零次或多次。 |
+ | 匹配前面的子表达式一次或多次。 |
. | 匹配除换行符 \n 之外的任何单字符。 |
[ | 标记一个中括号表达式的开始。 |
? | 匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。 |
\ | 将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例如, n 匹配字符 n。\n 匹配换行符。序列 \\ 匹配 '\' 字符,而 \( 则匹配 '(' 字符。 |
^ | 匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字符集合。 |
{ | 标记限定符表达式的开始。 |
| | 指明两项之间的一个选择。 |
限定符
限定符用来指定正则表达式的一个给定的组件必须要出现多少次才能满足匹配。见下表:
字符 | 描述 |
---|---|
* | 匹配前面的子表达式零次或多次。例如,foo* 能匹配 fo 以及 foooo。* 等价于{0,}。 |
+ | 匹配前面的子表达式一次或多次。例如,foo+ 能匹配 foo 以及 foooo,但不能匹配 fo。+ 等价于 {1,}。 |
? | 匹配前面的子表达式零次或一次。例如,Your(s)? 可以匹配 Your 或 Yours 中的Your 。? 等价于 {0,1}。 |
n 是一个非负整数。匹配确定的 n 次。例如,o{2} 不能匹配 for 中的 o,但是能匹配 foo 中的两个 o。 | |
n 是一个非负整数。至少匹配 n 次。例如,o{2,} 不能匹配 for 中的 o,但能匹配 foooooo 中的所有 o。o{1,} 等价于 o+。o{0,} 则等价于 o*。 | |
m 和 n 均为非负整数,其中 n 小于等于 m。最少匹配 n 次且最多匹配 m 次。例如,o{1,3} 将匹配 foooooo 中的前三个 o。o{0,1} 等价于 o?。注意,在逗号和两个数之间不能有空格。 |
以上我们知道了正则表达式的匹配机制。
std::regex
//代码来源于https://en.cppreference.com/w/cpp/header/regex
匹配
匹配是判断给定的字符串是否符合某个正则表达式。
// Simple regular expression matching
const std::string fnames[] = {"foo.txt", "bar.txt", "baz.dat", "zoidberg"};
const std::regex txt_regex("[a-z]+\\.txt");
for (const auto &fname : fnames) {
std::cout << fname << ": " << std::regex_match(fname, txt_regex) << '\n';
}
// Extraction of a sub-match
const std::regex base_regex("([a-z]+)\\.txt");
std::smatch base_match;
for (const auto &fname : fnames) {
if (std::regex_match(fname, base_match, base_regex)) {
// The first sub_match is the whole string; the next
// sub_match is the first parenthesized expression.
if (base_match.size() == 2) {
std::ssub_match base_sub_match = base_match[1];
std::string base = base_sub_match.str();
std::cout << fname << " has a base of " << base << '\n';
}
}
}
// Extraction of several sub-matches
const std::regex pieces_regex("([a-z]+)\\.([a-z]+)");
std::smatch pieces_match;
for (const auto &fname : fnames) {
if (std::regex_match(fname, pieces_match, pieces_regex)) {
std::cout << fname << '\n';
for (size_t i = 0; i < pieces_match.size(); ++i) {
std::ssub_match sub_match = pieces_match[i];
std::string piece = sub_match.str();
std::cout << " submatch " << i << ": " << piece << '\n';
}
}
}
结果:
foo.txt: 1
bar.txt: 1
baz.dat: 0
zoidberg: 0
foo.txt has a base of foo
bar.txt has a base of bar
foo.txt
submatch 0: foo.txt
submatch 1: foo
submatch 2: txt
bar.txt
submatch 0: bar.txt
submatch 1: bar
submatch 2: txt
baz.dat
submatch 0: baz.dat
submatch 1: baz
submatch 2: dat
搜索
std::string lines[] = {"Roses are #ff0000",
"violets are #0000ff",
"all of my base are belong to you"};
std::regex color_regex("#([a-f0-9]{2})"
"([a-f0-9]{2})"
"([a-f0-9]{2})");
// simple match
for (const auto &line : lines) {
std::cout << line << ": " << std::boolalpha
<< std::regex_search(line, color_regex) << '\n';
}
std::cout << '\n';
// show contents of marked subexpressions within each match
std::smatch color_match;
for (const auto& line : lines) {
if(std::regex_search(line, color_match, color_regex)) {
std::cout << "matches for '" << line << "'\n";
std::cout << "Prefix: '" << color_match.prefix() << "'\n";
for (size_t i = 0; i < color_match.size(); ++i)
std::cout << i << ": " << color_match[i] << '\n';
std::cout << "Suffix: '" << color_match.suffix() << "\'\n\n";
}
}
// repeated search (see also std::regex_iterator)
std::string log(R"(
Speed: 366
Mass: 35
Speed: 378
Mass: 32
Speed: 400
Mass: 30)");
std::regex r(R"(Speed:\t\d*)");
std::smatch sm;
while(regex_search(log, sm, r))
{
std::cout << sm.str() << '\n';
log = sm.suffix();
}
// C-style string demo
std::cmatch cm;
if(std::regex_search("this is a test", cm, std::regex("test")))
std::cout << "\nFound " << cm[0] << " at position " << cm.prefix().length();
结果
Roses are #ff0000: true
violets are #0000ff: true
all of my base are belong to you: false
matches for 'Roses are #ff0000'
Prefix: 'Roses are '
0: #ff0000
1: ff
2: 00
3: 00
Suffix: ''
matches for 'violets are #0000ff'
Prefix: 'violets are '
0: #0000ff
1: 00
2: 00
3: ff
Suffix: ''
Speed: 366
Speed: 378
Speed: 400
Found test at position 10
替换
std::string text = "Quick brown fox";
std::regex vowel_re("a|e|i|o|u");
// write the results to an output iterator
std::regex_replace(std::ostreambuf_iterator<char>(std::cout),
text.begin(), text.end(), vowel_re, "*");
// construct a string holding the results
std::cout << '\n' << std::regex_replace(text, vowel_re, "[$&]") << '\n';
结果
Q**ck br*wn f*x
Q[u][i]ck br[o]wn f[o]x