目录
  • 正则表达式
  • RE库组件
  • 正则表达式的使用
  • regex迭代器类型
    • smatch相关操作
  • 子表达式
    • 子表达式用于数据验证
    • 子匹配操作
  • regex_replace
    • 总结 

      正则表达式

      正则表达式(regular expression)是一种描述字符序列的方法,是一种极其强大的计算工具。

      C++正则表达式库(RE库)定义在<regex>中,它包含多个组件。

      RE库组件

        解释
      regex 表示有一个正则表达式的类
      regex_match 将一个字符序列与一个正则表达式匹配
      regex_search 寻找第一个与正则表达式匹配的子序列
      regex_replace 使用给定格式替换一个正则表达式
      sregex_iterator 迭代器适配器,调用regex_search来遍历一个string中所有匹配的子串
      smatch 容器类,保存在string中搜索的结果
      ssub_match string中匹配的子表达式的结果

      正则表达式的使用

      #include <regex>
      void test()
      {
          //查找不是在字符c之后的ei组合存在的单词
      	string pattern("[^c]ei");
      	pattern = "[[:alpha:]]*" + pattern + "[[:alpha:]]*";
      	regex r(pattern);
      	smatch results;
      	string test_str("receipt freind theif receive");
      	if (regex_search(test_str, results, r))
      		cout << results.str() << endl;//freind
      }
      

      regex迭代器类型

      上面的程序只能查找第一个匹配到的单词,如果想获得所有匹配,可以使用sregex_iterator

      for (sregex_iterator it(test_str.begin(),test_str.end(),r), end_it;it != end_it;++it) {
      		cout << it->str() << endl;
      }
      

      输出:

      freind
      theif

      for循环中定义了两个迭代器,it负责寻找匹配的单词,end_it是一个空迭代器,起到尾后迭代器的作用。

      解引用迭代器会得到一个匹配结果的smatch对象。

      除了得到匹配的smatch对象以外,还可以得到其上下文。

      for (sregex_iterator it(test_str.begin(),test_str.end(),r), end_it;it != end_it;++it) {
      	auto pos = it->prefix().length();
      	pos = pos > 40 ? pos - 40 : 0;
      	cout << it->prefix().str().substr(pos)
      		<< "[ " << it->str() << " ]"
      		<< it->suffix().str().substr(0, 40)
      		<< endl;
      }
      

      输出:

      receipt [ freind ] theif receive
       [ theif ] receive

      使用prefix和suffix函数可以得到匹配之前和之后的ssub_match对象。

      smatch相关操作

        解释
      m.ready() 若已通过regex_search或regex_match设置了m,则返回true;否则返回false
      m.size() 如果匹配失败,返回0;否则返回最近一次匹配的正则表达式中子表达式的数目
      m.empty() 若m.size()==0,返回true
      m.prefix() 一个ssub_match对象,表示当前匹配之前的序列
      m.suffix() 一个ssub_match对象,表示当前匹配之后的部分
      m.format() 格式化输出
      m.length(n) 第n个匹配的子表达式的大小
      m.position(n) 第n个子表达式距序列开始的距离
      m.str(n) 第n个子表达式匹配的string
      m[n] 对应第n个子表达式的ssub_match对象
      m.begin(),m.end() m中sub_match元素范围的迭代器
      m.cbegin(),m.cend() m中sub_match元素范围的常量迭代器

      这些操作也适用于cmatch、wsmatch、wcmatch和对应的子匹配对象。

      子表达式

      正则表达式中的模式通常包含一个或多个子表达式(subexpression)。

      一个子表达式是模式的一部分,本身也具有意义。

      正则表达式语法同常用小括号表示子表达式。

      eg: 可以使用子表达式来匹配文件扩展名

      regex r("([[:alnum:]]+)\\.(cpp|cxx|cc)$");
      

      现在模式中有两个小括号表示的子表达式:

      • ([[:alnum:]]+) 匹配一个或多个数字字母序列
      • (cpp|cxx|cc) 匹配cpp或cxx或cc等扩展名

      通过使用str(n)来打印子表达式

      if (regex_search(filename, results, r))
      		cout << results.str(1) << endl;//打印第一个子表达式
      

      参数0代表整个对应的匹配,参数1表示第一个子表达式。

      如,foo.cpp中,results.str(0)将保存foo.cpp,results.str(1)将保存foo。

      子表达式用于数据验证

      子表达式的一个常见用途是验证必须匹配特定格式的数据。

      eg:匹配联通号码

      中国联通号段:130、131、132、145、155、156、166、175、176、185、186、196

      使用开源工具Regulex实现正则表达式设计可视化。

      C++使用正则表达式的详细教程

      void test02()
      {
      	//匹配联通号码
      	string UnicomNumber("\\b(1)(3[0-2]|[4578]5|[5-9]6)(\\d{4})(\\d{4})\\b");
      	regex r(UnicomNumber);
      	string testNumbers("130123456789 23112345678 7602125 1320000 16512345678 14512345678 17612345678");
      	for (sregex_iterator it(testNumbers.begin(), testNumbers.end(), r), end_it;it != end_it;++it) {
      		cout << it->str() << endl;	
      	}
      }
      

      结果:

      1451234567817612345678

      解释:

      在模式UnicomNumber中,有4个子表达式

      子表达式索引号 子表达式 含义
      子表达式1 (1) 匹配1
      子表达式2 (3[0-2]|[4578]5|[5-9]6) 匹配30/31/32/45/55/75/85/56/66/76/86/96
      子表达式3 (\d{4}) 匹配任意4个数字
      子表达式4 (\d{4}) 匹配任意4个数字

      此外,"\b"匹配单词边界,可以理解为空格与单词的分界线。"\d"匹配任意数字。[]内表示多选一,{n}表示匹配n个,子表达式内"|"表示或。

      并且,在正则表达式语法中"\&;具有转义作用,在C++中也有转义作用,因此,为了得到正则表达式中的”\&;,需要在string中额外加一个"\&;。所以我们的表达式中会有"\\b"和"\\d"。

      在正则匹配过程中,迭代器查找每一个号码,进行分析

      号码 分析
      130123456789 多了一位数字,单词边界匹配失败
      23112345678 子表达式1匹配失败
      7602125 子表达式1匹配失败
      1320000 子表达式3匹配失败(或者说是边界匹配失败?)
      16512345678 子表达式2匹配失败
      14512345678 匹配成功
      17612345678 匹配成功

      子匹配操作

      ssub_match的相关操作

        解释
      matched 一个public bool成员,指出此ssub_match是否匹配了
      first,second public数据成员,指向匹配序列首元素和尾后迭代器
      length() 匹配的大小
      str() 匹配的string
      s = ssub 将ssub_match对象转化为string对象

      添加一段代码,测试一下matched成员

      for (sregex_iterator it(testNumbers.begin(), testNumbers.end(), r), end_it;it != end_it;++it) {
      	cout << it->str() << endl;	
      	cout << "\t" << (*it)[4].matched << endl;
      }
      

      结果

      14512345678
              1
      17612345678
              1

      这里的matched为true表示匹配到了,当然,UnicomNumber的子表达式并非是可选匹配的(用"?"跟在一个表达式后表示可以有1个或0个该表达式),所以它的四个子表达式全部匹配到了,若是可选表达式,可能会出现matched为false的情况。

      regex_replace

      正则表达式不仅用在查找给定序列方面,当我们想将查找到的序列替换为另一个序列时,可使用regex_replace。

      eg:格式化输出电话号码

      void test03()
      {
      	string UnicomNumber("\\b(1)(3[0-2]|[4578]5|[5-9]6)(\\d{4})(\\d{4})\\b");
      	regex r(UnicomNumber);
      	string fmt = "$1$2 $3 $4";
      	string number = "14512345678";
      	cout << regex_replace(number,r,fmt) << endl;
      }
      

      结果:

      145 1234 5678

      解释:

      使用"$"后跟子表达式的索引号来表示一个特定的子表达式。

      在"$1$2 $3 $4"中,希望子表达式1和2在一起,跟子表达式3和4之间都使用空格(" ")隔开。

      参考资料

      《C++ Primer 第5版》

      总结 

      声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。