regular_expression

C++ 正则表达式基础

简介

正则表达式描述了一种字符串匹配的模式。一般使用正则表达式主要是实现下面三个需求:

  • 匹配:检查一个串是否包含某种形式的子串;
  • 搜索:从某个串中取出符合特定形式的子串。
  • 替换:将匹配的子串替换;

常见用法

C++ 提供了 regex 相关组件,用户只需 #include <regex>。常用到的函数包括:regex_match、regex_replace、regex_search。这些函数主要涉及以下参数:

  • target sequence: 要查找的字符序列,可以是一对迭代器,也可以是 c-style 字符串或者 std::string;
  • regular expression:要匹配的正则表达式,默认是 ECMAScript syntax
  • match result:用来保存匹配结果的相关细节,常用的有 smatch, ssub_match;
  • replacement string: 用来替换指定格式的字符串。

关于写好一个正则表达式,可以参考 表达式全集,没必要死记!!!

匹配

regex_match 是全文匹配,用来判断整个 target sequence 是否和正则表达式相匹配。它有两种常用的接口:

  • bool regex_match(target_sequece, regular_expression);
  • bool regex_match(target_sequece, match_result, regular_expression)。

如果不关心匹配结果的具体信息,可以使用第一种。以下是一个具体示例:

string str = "http://www.baidu.com";
string reg = "([a-zA-z]+)://([^\\s]+)";
regex pattern(reg);

smatch result;
bool isMatch = regex_match(str, result, pattern);
if (isMatch) {
  for (int i = 0; i < result.size(); i++) {
    cout << result[i] << endl;
    // cout << result.str(i) << endl;
  }
} else {
  cout << "match fail" << endl;
}

注:smatch[0] 存储的是匹配的字符串,然后 1 - N,分别是匹配到的各个分组,分组是正则表达式中用括号 "()" 括起来的内容。

搜索

regex_search 是搜索匹配,用来判断 target sequence 中是否包含给定 pattern 的子串。常用的有两种:

  • bool regex_search(target_sequece, regular_expression);
  • bool regex_search(target_sequece, match_result, regular_expression)。

对于 regex_search 来说,它会在匹配到首个符合条件的子串就截止。因此,match_result 中仅包含首个子串的匹配信息。具体实例如下:

string str = "http://www.baidu.com, http://blog.csdn.net";
string reg = "([a-zA-z]+)://([^\\s]+)";
regex pattern(reg);
smatch result;
bool found = regex_search(str, result, pattern);
if (found) {
  cout << "result size = " << result.size() << endl;
  for (int i = 0; i < result.size(); i++) {
    cout << "result[" << i << "] = " << result.str(i) << endl;
  }
} else {
  cout << "regex_search(str, result, pattern) not found" << endl;
}

如果想搜索所有符合 pattern 的子串,需要结合字符串迭代器,每次搜索到结果后,将迭代器的起始值改为搜索到的结果的尾部,然后循环往复即可。

string str = "http://www.baidu.com, http://blog.csdn.net";
string reg = "([a-zA-z]+)://([^\\s]+)";
regex pattern(reg);
smatch result;

string::const_iterator iterStart = str.begin();
string::const_iterator iterEnd = str.end();
while (regex_search(iterStart, iterEnd, result, pattern)) {
  cout << "result size = " << result.size() << endl;
  for (int i = 0; i < result.size(); i++) {
    cout << "result[" << i << "] = " << result.str(i) << endl;
  }
  // end iterator of the current match string
  iterStart = result[0].second;
}

【注】:smatch[i].first 和 smatch[i].second 分别指向所匹配子串的首尾。

替换

regex_replace 是替换匹配。用来将 target sequence 中的匹配到的子串s1进行替换为s2。常用的有以下两种:

  • string regex_place(target_sequence, regular_expression, replace_string);
  • regex_place(output, target_sequence, regular_expression, replace_string)。

以下是一个示例:

string trimSpace(const string &str) {
  return regex_replace(str, regex("(^\\s*)|(\\s*$)"), "");
}

进阶

regex 迭代器

在上文中,我们使用 regex_search 搜索所有的匹配的子串时,需要循环地调用 regex_search 并手动更新 iterStart

其实,C++ 11 标准库支持了 sregex_iterator,它可以帮助我们获得所有匹配,从而简化代码编写。下面给出一个具体地示例:

string str = "http://www.baidu.com, http://blog.csdn.net";
string reg = "([a-zA-z]+)://([^\\s]+)";
regex pattern(reg);

for (sregex_iterator it(str.begin(), str.end(), pattern), end_it;
      it != end_it; ++it) {
  cout << "result size = " << it->size() << endl;
  for (int i = 0; i < it->size(); i++) {
    cout << "result[" << i << "] = " << it->str(i) << endl;
  }
}

当我们将一个 sregex_iterator 绑定到一个 string 和一个 regex 对象时,迭代器自动定位到给定 string 中第一个匹配位置。即,sregex_iterator 构造函数对给定 stringregex 调用 regex_search。当我们解用迭代器时,会得到一个对应最近一次搜索结果地 smatch 对象。当我们递增迭代器时,它调用 regex_search 在输入 string 中查找下一个匹配。

match result

常用于存储匹配信息的数据结构有 smatch, cmatch。它们均是 match_results 的实例。以下是相关代码:

typedef match_results<const char*> cmatch;
typedef match_results<string::const_iterator> smatch;

match_results 是一个 container-like 的类,用于存储所有匹配的结果,具体以下特点:

  • 其中每一个匹配都是一个 sub_match 对象, 而且是常量属性;
  • 第一个 sub_match 元素对应整个匹配,接下来的是子匹配,即用 “()” 括起来的部分;
  • 支持类似顺序容器的操作,支持下标访问,具体参考cplusplus match_results
  • sub_match 继承自 pairsub_match.firstsub_match.second 指向所匹配的子串的首和尾。

异常

用户编写的正则表达式存在错误,在编译期不会被检测出异常,只有在运行时标准库会抛出一个异常类型为 regex_error 的异常。

try {
  regex r("[[:alnum:]+\\.(cpp|cxx|cc)$");
} catch (const std::regex_error& e) {
  std::cout << e.what() << "\ncode:" << e.code() << '\n';
}

输出结果如下:

Unexpected character in bracket expression.
code:4

match_flag_type

默认采用 regex::ECMAScript 标志,其他常用的标志如下:

标志含义
icase在匹配过程中忽略大小写
nosubs不保存匹配的子表达式
optimize执行速度优先于构造速度

合理的使用这些标志,可以简化 regex pattern 的写法,或者提升匹配的速度。

原生字符

在正则表达式中,有些字符是有特殊字符。因此如果想要匹配这些字符,那么就必须使用反斜杠进行转义。比如 $, 就需要表示为 \\$, 第一层是 C++ 语言本身的转义,第二层是正则表达式的转义。

自 C++11 开始支持原生字符,这样一来就可以避免正则表达式中的 \\, 仅需保留正则表达式的转义。原生字符串字面值的语法是 R"(...)",其中 ... 是字符串的内容。以下是一个具体的示例:

std::string s("there is a subsequence in the string\n");
std::regex e(R"(\b(sub)([^ ]*))");  // matches words beginning by "sub"
// std::regex e("\\b(sub)([^ ]*)");
std::cout << std::regex_replace(s, e, "sub-$2");

tips

  • 避免在循环内使用正则表达式,应该在循环外创建它。构造一个 regex 对象以及向一个已存在的 regex 赋予一个新的正则表达式是非常耗时的。

参考

[1]. regex reference
[2]. 史上最全!C++ 与正则表达式,一次看个够
[3]. C++ 正则表达式速查手册
[4]. 表达式全集
[5]. Regex C++

  • 36
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值