精华内容
参与话题
问答
  • 正则表达式(regex)入门

    万次阅读 多人点赞 2018-04-04 10:23:46
    正则表达式,又称正规表示法、常规表示法(英语:Regular Expression,在代码中常简写为regex、regexp或RE),它是计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列符合某个句法规则的字符串。...

    什么是正则表达式呢?
    正则表达式,又称正规表示法、常规表示法(英语:Regular Expression,在代码中常简写为regex、regexp或RE),它是计算机科学的一个概念。正则表达式使用单个字符串来描述、匹配一系列符合某个句法规则的字符串。几乎在各种计算机编程语言中都有用到。可以分为普通正则表达式、扩展正则表达式、高级正则表达式。普通正则表达式在linux shell中常用到,高级正则表达式语法规范,基本由perl演化而来。目前常见程序语言(php,perl,python,java,c#)都支持高级正则表达式。

    我们为什么要学习正则表达式?
    高级程序语言的正则表达式几乎都从perl语言发展而来,因此,语法几乎一致。你学好了,一门正则表达式语言。几乎在所有程序语言中就可以用到。就像,我知道sql语法,后端mysql,mssql几乎都通用。这个也是我们需要学好正则表达式一个原因,通用性。另外一个原因是:正则表达式强大的文本匹配功能。很多文本匹配处理,如果没有正则表达式,还真的很难做出来。如:从一段字符串,读出手机号格式,我们如果用字符串查找,需要做循环,需要写判断。估计耗费不少代码,开发时间。如果用正则表达式,就一行代码就可以了。匹配所有成对的:html标签,如果要做这个,我们发现非常复杂,要处理层次,要匹配标签。一般同人短短几个小时可能完成不了。如果用正则表达式,估计也就几分钟而已。

    正则表达式字符串格式
    既然我们知道正则表达式重要性,通用性。那么我们对常见格式可以了解下。一般正则表达式由:普通字符+特殊字符(元字符)一起组成的字符串。如:匹配“ab开头,后面紧跟数字字符串“ “ab\d+” 这其中ab就是普通字符,\d代表可以是0-9数字,+代表前面字符可以出现1次或以上。哈哈,看起来还真的很容易吧!

    正则表达式无论是普通还是扩展还是高级正则表达式。不同之处,可能在特殊字符方面有些不同。很多特殊字符,可以组合,形成一套新匹配规则。这里就不说太深了。我们一般只要知道它的常见元字符。基本上常见正则表达式就可以写出来了。

    以下是javascript 正则表达式常见的元字符:

     

    字符 描述
    \ 将下一个字符标记为一个特殊字符、或一个原义字符、或一个 后向引用、或一个八进制转义符。例如,'n' 匹配字符 "n"。'\n' 匹配一个换行符。序列 ‘\\' 匹配 "\" 而 "\(" 则匹配 "("。
    ^ 匹配输入字符串的开始位置。如果设置了 RegExp 对象的 Multiline 属性,^ 也匹配 ‘\n' 或 ‘\r' 之后的位置。
    $ 匹配输入字符串的结束位置。如果设置了RegExp 对象的 Multiline 属性,$ 也匹配 ‘\n' 或 ‘\r' 之前的位置。
    * 匹配前面的子表达式零次或多次。例如,zo* 能匹配 "z" 以及 "zoo"。 * 等价于{0,}。
    + 匹配前面的子表达式一次或多次。例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。+ 等价于 {1,}。
    ? 匹配前面的子表达式零次或一次。例如,"do(es)?" 可以匹配 "do" 或 "does" 中的"do" 。? 等价于 {0,1}。
    {n} n 是一个非负整数。匹配确定的 n 次。例如,'o{2}' 不能匹配 "Bob" 中的 ‘o',但是能匹配 "food" 中的两个 o。
    {n,} n 是一个非负整数。至少匹配n 次。例如,'o{2,}' 不能匹配 "Bob" 中的 ‘o',但能匹配 "foooood" 中的所有 o。'o{1,}' 等价于 ‘o+'。'o{0,}' 则等价于 ‘o*'。
    {n,m} m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。刘, "o{1,3}" 将匹配 "fooooood" 中的前三个 o。'o{0,1}' 等价于 ‘o?'。请注意在逗号和两个数之间不能有空格。
    ? 当该字符紧跟在任何一个其他限制符 (*, +, ?, {n}, {n,}, {n,m}) 后面时,匹配模式是非贪婪的。非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。例如,对于字符串 "oooo",'o+?' 将匹配单个 "o",而 ‘o+' 将匹配所有 ‘o'。
    . 匹配除 "\n" 之外的任何单个字符。要匹配包括 ‘\n' 在内的任何字符,请使用象 ‘[.\n]‘ 的模式。
    (pattern) 匹配pattern 并获取这一匹配。所获取的匹配可以从产生的 Matches 集合得到,在VBScript 中使用 SubMatches 集合,在JScript 中则使用 $0…$9 属性。要匹配圆括号字符,请使用 ‘\(‘ 或 ‘\)'。
    (?:pattern) 匹配 pattern 但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用 "或" 字符 (|) 来组合一个模式的各个部分是很有用。例如, ‘industr(?:y|ies) 就是一个比 ‘industry|industries' 更简略的表达式。
    (?=pattern) 正向预查,在任何匹配 pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如, ‘Windows (?=95|98|NT|2000)' 能匹配 "Windows 2000" 中的 "Windows" ,但不能匹配 "Windows 3.1" 中的 "Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。
    (?!pattern) 负向预查,在任何不匹配Negative lookahead matches the search string at any point where a string not matching pattern 的字符串开始处匹配查找字符串。这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。例如'Windows (?!95|98|NT|2000)' 能匹配 "Windows 3.1" 中的 "Windows",但不能匹配 "Windows 2000" 中的 "Windows"。预查不消耗字符,也就是说,在一个匹配发生后,在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始
    x|y 匹配 x 或 y。例如,'z|food' 能匹配 "z" 或 "food"。'(z|f)ood' 则匹配 "zood" 或 "food"。
    [xyz] 字符集合。匹配所包含的任意一个字符。例如, ‘[abc]‘ 可以匹配 "plain" 中的 ‘a'。
    [^xyz] 负值字符集合。匹配未包含的任意字符。例如, ‘[^abc]‘ 可以匹配 "plain" 中的'p'。
    [a-z] 字符范围。匹配指定范围内的任意字符。例如,'[a-z]‘ 可以匹配 ‘a' 到 ‘z' 范围内的任意小写字母字符。
    [^a-z] 负值字符范围。匹配任何不在指定范围内的任意字符。例如,'[^a-z]‘ 可以匹配任何不在 ‘a' 到 ‘z' 范围内的任意字符。
    \b 匹配一个单词边界,也就是指单词和空格间的位置。例如, ‘er\b' 可以匹配"never" 中的 ‘er',但不能匹配 "verb" 中的 ‘er'。
    \B 匹配非单词边界。'er\B' 能匹配 "verb" 中的 ‘er',但不能匹配 "never" 中的 ‘er'。
    \cx 匹配由x指明的控制字符。例如, \cM 匹配一个 Control-M 或回车符。 x 的值必须为 A-Z 或 a-z 之一。否则,将 c 视为一个原义的 ‘c' 字符。
    \d 匹配一个数字字符。等价于 [0-9]。
    \D 匹配一个非数字字符。等价于 [^0-9]。
    \f 匹配一个换页符。等价于 \x0c 和 \cL。
    \n 匹配一个换行符。等价于 \x0a 和 \cJ。
    \r 匹配一个回车符。等价于 \x0d 和 \cM。
    \s 匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。
    \S 匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。
    \t 匹配一个制表符。等价于 \x09 和 \cI。
    \v 匹配一个垂直制表符。等价于 \x0b 和 \cK。
    \w 匹配包括下划线的任何单词字符。等价于'[A-Za-z0-9_]‘。
    \W 匹配任何非单词字符。等价于 ‘[^A-Za-z0-9_]‘。
    \xn 匹配 n,其中 n 为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如, ‘\x41′ 匹配 "A"。'\x041′ 则等价于 ‘\x04′ & "1"。正则表达式中可以使用 ASCII 编码。.
    \num 匹配 num,其中 num 是一个正整数。对所获取的匹配的引用。例如,'(.)\1′ 匹配两个连续的相同字符。
    \n 标识一个八进制转义值或一个后向引用。如果 \n 之前至少 n 个获取的子表达式,则 n 为后向引用。否则,如果 n 为八进制数字 (0-7),则 n 为一个八进制转义值。
    \nm 标识一个八进制转义值或一个后向引用。如果 \nm 之前至少有is preceded by at least nm 个获取得子表达式,则 nm 为后向引用。如果 \nm 之前至少有 n 个获取,则 n 为一个后跟文字 m 的后向引用。如果前面的条件都不满足,若 n 和 m均为八进制数字 (0-7),则 \nm 将匹配八进制转义值 nm。
    \nml 如果 n 为八进制数字 (0-3),且 m 和 l 均为八进制数字 (0-7),则匹配八进制转义值 nml。
    \un 匹配 n,其中 n 是一个用四个十六进制数字表示的 Unicode 字符。例如, \u00A9 匹配版权符号 (?)。

     

    从上面元字符里面,我们看到,很多元字符,实际上可以代表一组普通字符。因此,我们要匹配一些字符串,正则表达式往往会有很多种。如:匹配0-9数字,可以用[0-9],\d,[0123456789] ,这样3种都可以,条条大路通罗马,都是对的。那么那一种正则表达式更好呢,性能更高呢,匹配速度更快呢?通过10万次 循环匹配,发现几种几乎相差不大,\d速度比[0-9快,[0-9]比[0123456789]快。从正则表达式精简]程度方面,\d最简单。使用时候,我们尽量用代表字符集元字符去匹配。精简且速度快!

    怎么样书写正则表达式呢?
    我们写正则表达式,都是从分析匹配字符串特点开始,然后逐步补充其它元字符,普通字符。匹配从左到右。

    例如:我们要匹配一个手机号码。

    1. 分析字符串特点,手机号码是数字,并且是以1开头,11位长

    2.可以写”1\d”   1开头,后面跟着数字 也可以是:1[0-9]

    3.数字长度是11位 ,继续补充1\d{10} ,后面数字长11字符,也可以是:1[0-9]{10} ;{}里面数字,表示它左边字符可以重复出现次数

    4.所有字符必须是11位,因此头尾直接必须满足条件,因此可以是:^1\d{10}$ 了。

    例如:我们匹配QQ号码

    1.分析QQ号码特点是,号码是 最少是5位数,首位字符非0,最大长度,目前到11位了

    2.可以先定义首位字符,[1-9]\d    首位字符是1到9,后面是字符

    3.后面字符个数在4到10位 [1-9]\d{4,10}

    4.所有字符串必须都满足上面匹配,因为可以写成:^[1-9]\d{4,10}$

    例如:匹配IP地址

    1.分析ip结构是,每节 0-255,中间用”.”分割,一共有4节

    2.首先我们写第一个0-255 ,可以分解为0-9 一位数,10-99两位数,100-199三位数,200-249三位数第2节,250-255第四节

    [0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]   “|”表示或者,计算优先级最低,左右两边可以是多个元字符普通字符组合字符串为一个整体。

    3.这样的字符,有三次重复,中间加”.” ,所以结果是:

    [0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]\. ,因为是点字符是元字符,所有需要转义。这样是不是可以了呢,我们发现有问题,”|”优先级最低,这样会把最后\.字符表,组合为:“25[0-5] \.”了。因此,应该是前面几种情况,后面跟个”.”字符,正确是:([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\. ,这样就达到要求了。我们会发现,实际上每家一个()字符,都一个子匹配,会在匹配结果里面出现()内容。这里我们加()目的是,让优先计算,因此不需要里面子匹配内容。我们可以加忽略子匹配内容字符:?: ,结果将变为:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.

    4.一段已经匹配到了,然后我们这样需要重复三次,我们可以直接重复上一个表达式3次:

    方法一:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.

    方法二:把第一段作为分组,重复3次    ((?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3} ,然后同样忽略子匹配结果,可以变为:

    (?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3} 哈哈,看到这个表达式是不是很晕了,其实一个长的表达式,都是从一点一点加上去的。这个利用到,重复次数,将结果简化不少了。

    5.最后还有一段0-255匹配

    (?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]) ,也就是在后面加多一个0-255匹配即可,然后在上面再加上头尾限定符,变成了:^(?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$

    展开全文
  • regex里的基本常用函数

    千次阅读 2010-08-31 16:39:00
    字符串匹配 要确定一行字符串是否与指定的正则表达式匹配,使用regex_match。 下面这个代码可以验证szStr字串(定义在上面)是否与szReg匹配。 { //字符串匹配  boost::regex reg( szReg ); ...

    1.字符串匹配
    要确定一行字符串是否与指定的正则表达式匹配,使用regex_match。
    下面这个代码可以验证szStr字串(定义在上面)是否与szReg匹配。

    {    //字符串匹配
        boost::regex reg( szReg );
        bool r=boost::regex_match( szStr , reg);
        assert(r); //是否匹配
    }
        {    //字符串匹配
            boost::regex reg( szReg );
            bool r=boost::regex_match( szStr , reg);
            assert(r); //是否匹配
        }
    boost::regex的构造函数中还可以加入标记参数用于指定它的行为,如:

    //指定使用perl语法(默认),忽略大小写。
    boost::regex reg1( szReg, boost::regex::perl|boost::regex::icase );
    //指定使用POSIX扩展语法(其实也差不多)
    boost::regex reg2( szReg, boost::regex::extended );
    //指定使用perl语法(默认),忽略大小写。
    boost::regex reg1( szReg, boost::regex::perl|boost::regex::icase );
    //指定使用POSIX扩展语法(其实也差不多)
    boost::regex reg2( szReg, boost::regex::extended );

    下面这个代码不仅验证是否匹配,而且可以从中提取出正则表达式括号对应的子串。

    {    //提取子串
        boost::cmatch mat;
        boost::regex reg( szStr );
        bool r=boost::regex_match( szStr, mat, reg);
        if(r) //如果匹配成功
        {
            //显示所有子串
            for(boost::cmatch::iterator itr=mat.begin(); itr!=mat.end(); ++itr)
            {
                //       指向子串对应首位置        指向子串对应尾位置          子串内容
                cout << itr->first-szStr << ' ' << itr->second-szStr << ' ' << *itr << endl;
            }
        }
        //也可直接取指定位置信息
        if(mat[4].matched) cout << "Path is" << mat[4] << endl;
    }
        {    //提取子串
            boost::cmatch mat;
            boost::regex reg( szStr );
            bool r=boost::regex_match( szStr, mat, reg);
            if(r) //如果匹配成功
            {
                //显示所有子串
                for(boost::cmatch::iterator itr=mat.begin(); itr!=mat.end(); ++itr)
                {
                    //       指向子串对应首位置        指向子串对应尾位置          子串内容
                    cout << itr->first-szStr << ' ' << itr->second-szStr << ' ' << *itr << endl;
                }
            }
            //也可直接取指定位置信息
            if(mat[4].matched) cout << "Path is" << mat[4] << endl;
        }
    其中,boost::cmatch是一个针对C字符串的特化版本,它还有另三位兄弟,如下:

    typedef match_results<const char*> cmatch;
    typedef match_results<std::string::const_iterator> smatch;
    typedef match_results<const wchar_t*> wcmatch;
    typedef match_results<std::wstring::const_iterator> wsmatch;
    可以把match_results看成是一个sub_match的容器,同时它还提供了format方法来代替regex_format函数。
    一个sub_match就是一个子串,它从std::pair<BidiIterator, BidiIterator>继承而来,这个迭代器pair里的first和second分别指向了这个子串开始和结尾所在位置。同时,sub_match又提供了str(),length()方法来返回整个子串。
     

    2.查找字符串
    regex_match只验证是否完全匹配,如果想从一大串字符串里找出匹配的一小段字符串(比如从网页文件里找超链接),这时就要使用regex_search了。
    下面这段代码从szStr中找数字

    { //查找
        boost::cmatch mat;
        boost::regex reg( "//d+" );    //查找字符串里的数字
        if(boost::regex_search(szStr, mat, reg))
        {
            cout << "searched:" << mat[0] << endl;
        }
    }
        { //查找
            boost::cmatch mat;
            boost::regex reg( "//d+" );    //查找字符串里的数字
            if(boost::regex_search(szStr, mat, reg))
            {
                cout << "searched:" << mat[0] << endl;
            }
        } 

    3.替换
    regex_replace提供了简便的方法来部分替换源字符串
    正则表达式中,使用$1~$9(或/1~/9)表示第几个子串,$&表示整个串,$`表示第一个串,$'表示最后未处理的串。

    { //替换1,把上面的HTTP的URL转成FTP的
        boost::regex reg( szReg );
        string s = boost::regex_replace( string(szStr), reg, "ftp://$2$5");
        cout << "ftp site:"<< s << endl;
    }
        { //替换1,把上面的HTTP的URL转成FTP的
            boost::regex reg( szReg );
            string s = boost::regex_replace( string(szStr), reg, "ftp://$2$5");
            cout << "ftp site:"<< s << endl;
        }
    正则表达式中,使用(?1~?9新字串)表示把第几个子串替换成新字串

    { //替换2,使用format_all参数把<>&全部转换成网页字符
        string s1 = "(<)|(>)|(&)";
        string s2 = "(?1&lt;)(?2&gt;)(?3&amp;)";
        boost::regex reg( s1 );
        string s = boost::regex_replace( string("cout << a&b << endl;"), reg, s2, boost::match_default | boost::format_all);
        cout << "HTML:"<< s << endl;
    }
    4.使用regex_iterator查找
        对应于C字符串和C++字符串以及宽字符,regex_iterator同样也有四个特化:

        typedef regex_iterator<const char*> cregex_iterator;
        typedef regex_iterator<std::string::const_iterator> sregex_iterator;
        typedef regex_iterator<const wchar_t*> wcregex_iterator;
        typedef regex_iterator<std::wstring::const_iterator> wsregex_iterator;
        这个迭代器的value_type定义是一个match_results。

    { //使用迭代器找出所有数字
        boost::regex reg( "//d+" );    //查找字符串里的数字
        boost::cregex_iterator itrBegin(szStr, szStr+strlen(szStr), reg);
        boost::cregex_iterator itrEnd;
        for(boost::cregex_iterator itr=itrBegin; itr!=itrEnd; ++itr)
        {
                //       指向子串对应首位置        指向子串对应尾位置          子串内容
                cout << (*itr)[0].first-szStr << ' ' << (*itr)[0].second-szStr << ' ' << *itr << endl;
        }
    }
        { //使用迭代器找出所有数字
            boost::regex reg( "//d+" );    //查找字符串里的数字
            boost::cregex_iterator itrBegin(szStr, szStr+strlen(szStr), reg);
            boost::cregex_iterator itrEnd;
            for(boost::cregex_iterator itr=itrBegin; itr!=itrEnd; ++itr)
            {
                    //       指向子串对应首位置        指向子串对应尾位置          子串内容
                    cout << (*itr)[0].first-szStr << ' ' << (*itr)[0].second-szStr << ' ' << *itr << endl;
            }
        }
        Boost.Regex也提供了make_regex_iterator函数简化regex_iterator的构造,如上面的itrBegin可以写成:

    itrBegin = make_regex_iterator(szStr,reg);5.使用regex_token_iterator拆分字符串
        它同样也有四个特化,形式和上面类似,就不再写一遍骗篇幅了。
        这个迭代器的value_type定义是一个sub_match。

    { //使用迭代器拆分字符串
        boost::regex reg("/");  //按/符拆分字符串
        boost::cregex_token_iterator itrBegin(szStr, szStr+strlen(szStr), reg,-1);
        boost::cregex_token_iterator itrEnd;
        for(boost::cregex_token_iterator itr=itrBegin; itr!=itrEnd; ++itr)
        {
            cout << *itr << endl;
        }
    }
        { //使用迭代器拆分字符串
            boost::regex reg("/");  //按/符拆分字符串
            boost::cregex_token_iterator itrBegin(szStr, szStr+strlen(szStr), reg,-1);
            boost::cregex_token_iterator itrEnd;
            for(boost::cregex_token_iterator itr=itrBegin; itr!=itrEnd; ++itr)
            {
                cout << *itr << endl;
            }
        }
        Boost.Regex也提供了make_regex_token_iterator函数简化regex_token_iterator的构造,最后的那个参数-1表示以reg为分隔标志拆分字符串,如果不是-1则表示取第几个子串,并且可以使用数组来表示同时要取几个子串,例如:

    { //使用迭代器拆分字符串2
        boost::regex reg("(.)/(.)");  //取/的前一字符和后一字符(这个字符串形象貌似有点邪恶-_-)
        int subs[] = {1,2};        // 第一子串和第二子串
        boost::cregex_token_iterator itrBegin = make_regex_token_iterator(szStr,reg,subs); //使用-1参数时拆分,使用其它数字时表示取第几个子串,可使用数组取多个串
        boost::cregex_token_iterator itrEnd;
        for(boost::cregex_token_iterator itr=itrBegin; itr!=itrEnd; ++itr)
        {
            cout << *itr << endl;
        }
    }
    完整测试代码:
    #include <iostream>
    #include <boost/regex.hpp>
     
    using namespace std;
    int main(int argc, char* argv[])
    {    //( 1 )   ((  3  )  2 )((  5 )4)(    6    )   
        //(/w+)://((/w+/.)*/w+)((//w*)*)(//w+/./w+)?
        //^协议://网址(x.x...x)/路径(n个/字串)/网页文件(xxx.xxx)
        const char *szReg = "(//w+)://((//w+//.)*//w+)((///w*)*)(///w+//.//w+)?";
        const char *szStr = "http://www.cppprog.com/2009/0112/48.html";
     
        {    //字符串匹配
            boost::regex reg( szReg );
            bool r=boost::regex_match( szStr , reg);
            assert(r);
        }
     
        {    //提取子串
            boost::cmatch mat;
            boost::regex reg( szReg );
            bool r=boost::regex_match( szStr, mat, reg);
            if(r) //如果匹配成功
            {
                //显示所有子串
                for(boost::cmatch::iterator itr=mat.begin(); itr!=mat.end(); ++itr)
                {
                    //       指向子串对应首位置        指向子串对应尾位置          子串内容
                    cout << itr->first-szStr << ' ' << itr->second-szStr << ' ' << *itr << endl;
                }
            }
            //也可直接取指定位置信息
            if(mat[4].matched) cout << "Path is" << mat[4] << endl;
        }
     
        { //查找
            boost::cmatch mat;
            boost::regex reg( "//d+" );    //查找字符串里的数字
            if(boost::regex_search(szStr, mat, reg))
            {
                cout << "searched:" << mat[0] << endl;
            }
        }
     
        { //替换
            boost::regex reg( szReg );
            string s = boost::regex_replace( string(szStr), reg, "ftp://$2$5");
            cout << "ftp site:"<< s << endl;
        }
        { //替换2,把<>&转换成网页字符
            string s1 = "(<)|(>)|(&)";
            string s2 = "(?1&lt;)(?2&gt;)(?3&amp;)";
            boost::regex reg( s1 );
            string s = boost::regex_replace( string("cout << a&b << endl;"), reg, s2, boost::match_default | boost::format_all);
            cout << "HTML:"<< s << endl;
        }
     
        { //使用迭代器找出所有数字
            boost::regex reg( "//d+" );    //查找字符串里的数字
            boost::cregex_iterator itrBegin = make_regex_iterator(szStr,reg); //(szStr, szStr+strlen(szStr), reg);
            boost::cregex_iterator itrEnd;
            for(boost::cregex_iterator itr=itrBegin; itr!=itrEnd; ++itr)
            {
                    //       指向子串对应首位置        指向子串对应尾位置          子串内容
                    cout << (*itr)[0].first-szStr << ' ' << (*itr)[0].second-szStr << ' ' << *itr << endl;
            }
        }
     
        { //使用迭代器拆分字符串
            boost::regex reg("/");  //按/符拆分字符串
            boost::cregex_token_iterator itrBegin = make_regex_token_iterator(szStr,reg,-1); //使用-1参数时拆分,使用其它数字时表示取第几个子串,可使用数组取多个串
            boost::cregex_token_iterator itrEnd;
            for(boost::cregex_token_iterator itr=itrBegin; itr!=itrEnd; ++itr)
            {
                cout << *itr << endl;
            }
        }
     
        { //使用迭代器拆分字符串2
            boost::regex reg("(.)/(.)");  //取/的前一字符和后一字符(这个字符串形象貌似有点邪恶-_-)
            int subs[] = {1,2};        // 第一子串和第二子串
            boost::cregex_token_iterator itrBegin = make_regex_token_iterator(szStr,reg,subs); //使用-1参数时拆分,使用其它数字时表示取第几个子串,可使用数组取多个串
            boost::cregex_token_iterator itrEnd;
            for(boost::cregex_token_iterator itr=itrBegin; itr!=itrEnd; ++itr)
            {
                cout << *itr << endl;
            }
        }
     
     
        cin.get();
        return 0;
    }


    本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/stormlk1983/archive/2009/07/10/4337793.aspx

    展开全文
  • Regex 用法详解

    千次阅读 2008-06-18 16:12:00
    转自http://hi.baidu.com/%5F%E2%64%5F%B7%B3%5F%DE%B2%C2%D2/blog/item/ce5f8c2a4075ed29d42af1b3.html Regex 头文件:
    
    

    Regex

    头文件: "boost/regex.hpp"

    正则表达式被封装为一个类型 basic_regex的对象。我们将在下一节更深入地讨论正则表达式如何被编译和分析,这里我们首先粗略地看看 basic_regex ,以及这个库中三个最重要的算法。

    成员函数

    这个构造函数接受一个包含正则表达式的字符序列,还有一个参数用于指定使用正则表达式时的选项,例如是否忽略大小写。如果p中的正则表达式无效,则抛出一个 bad_expressionregex_error 的异常。注意这两个异常其实是同一个东西;在写这本书之时,尚未改变当前使用的名字 bad_expression ,但下一个版本的Boost.Regex将会使用 regex_error.

    这个成员函数是一个谓词,当basic_regex实例没有包含一个有效的正则表达式时返回 true ,即它被赋予一个空的字符序列时。

    mark_count 返回regex中带标记子表达式的数量。带标记子表达式是指正则表达式中用圆括号括起来的部分。匹配这个子表达式的文本可以通过调用某个正则表达式算法而获得。

    返回一个位掩码,其中包含这个basic_regex所设置的选项标志。例如标志 icase, 表示正则表达式忽略大小写,标志 JavaScript, 表示regex使用JavaScript的语法。

    不要使用类型 basic_regex来定义变量,你应该使用这两个typedef中的一个。这两个类型,regexwregex, 是两种字符类型的缩写,就如 stringwstringbasic_string<char>basic_string<wchar_t>的缩写一样。这种相似性是不一样的,某种程度上,regex 是一个特定类型的字符串的容器。

    普通函数

    regex_match 判断一个正则表达式(参数 e)是否匹配整个字符序列 str. 它主要用于验证文本。注意,这个正则表达式必须匹配被分析串的全部,否则函数返回 false. 如果整个序列被成功匹配,regex_match 返回 True.

    regex_search 类似于 regex_match, 但它不要求整个字符序列完全匹配。你可以用 regex_search 来查找输入中的一个子序列,该子序列匹配正则表达式 e.

    regex_replace 在整个字符序列中查找正则表达式e的所有匹配。这个算法每次成功匹配后,就根据参数fmt对匹配字符串进行格式化。缺省情况下,不匹配的文本不会被修改,即文本会被输出但没有改变。

    这三个算法都有几个不同的重载形式:一个接受 const charT* (charT 为字符类型), 另一个接受 const basic_string<charT>&, 还有一个重载接受两个双向迭代器作为输入参数。

    用法

    要使用Boost.Regex, 你需要包含头文件"boost/regex.hpp". Regex是本书中两个需要独立编译的库之一(另一个是Boost.Signals)。你会很高兴获知如果你已经构建了Boost— —那只需在命令提示符下打一行命令——就可以自动链接了(对于Windows下的编译器),所以你不需要为指出那些库文件要用而费心。

    你要做的第一件事就是声明一个类型 basic_regex 的变量。这是该库的核心类之一,也是存放正则表达式的地方。创建这样一个变量很简单;只要将一个含有你要用的正则表达式的字符串传递给构造函数就行了。

    这个正则表达式具有三个有趣的特性。第一个是,用圆括号把一个子表达式括起来,这样可以稍后在同一个正则表达式中引用它,或者取出匹配它的文本。我们稍后会详细讨论它,所以如果你还不知道它有什么用也不必担心。第二个是,通配符(wildcard)字符,点。这个通配符在正则表达式中有非常特殊的意义;这可以匹配任意字符。最后一个是,这个表达式用到了一个重复符,*, 称为Kleene star, 表示它前面的表达式可以被匹配零次或多次。这个正则表达式已可以用于某个算法了,如下:

    如你所见,你把正则表达式和要分析的字符串传递给算法 regex_match. 如果的确存在与正则表达式的匹配,则该函数调用返回结果 true ;否则,返回 false. 在这个例子中,结果是 false, 因为 regex_match 仅当整个输入数据被正则表达式成功匹配时才返回 true 。你知道为什么是这样吗?再看一下那个正则表达式。第一个字符是大写的 A, 很明显能够匹配这个表达式的第一个字符在哪。所以,输入的一部分"A and beyond."可以匹配这个表达式,但这不是整个输入。让我们试一下另一个输入字符串。

    这一次,regex_match 返回 true. 当正则表达式引擎匹配了 A, 它接着看后续有什么。在我们的regex变量中,A 后跟一个通配符和一个Kleene star, 这意味着任意字符可以被匹配任意次。因而,分析过程开始扔掉输入字符串的剩余部分,即匹配了输入的所有部分。

    接下来,我们看看如何使用regexes 和 regex_match 来进行数据验证。

    验证输入

    正则表达式常用于对输入数据的格式进行验证。应用软件通常要求输入符合某种结构。考虑一个应用软件,它要求输入一定要符合如下格式,"3个数字, 一个单词, 任意字符, 2个数字或字符串"N/A," 一个空格, 然后重复第一个单词." 手工编写代码来验证这个输入既沉闷又容易出错,而且这些格式还很可能会改变;在你弄明白之前,可能就需要支持其它的格式,你精心编写的分析器可能就需要修改并重新调试。让我们写出一个可以验证这个输入的正则表达式。首先,我们需要一个匹配3个数字的表达式。对于数字,我们应该使用一个特别的缩写,/d。要表示它被重复3次,需要一个称为bounds operator的特定重复,它用花括号括起来。把这两个合起来,就是我们的正则表达式的开始部分了。

    注意,我们需要在转义字符(/)之前加一个转义字符,即在我们的字符串中,缩写 /d 变成了 //d 。这是因为编译器会把第一个/当成转义字符扔掉;我们需要对/进行转义,这样/才可以出现在我们的正则表达式中。

    接下来,我们需要定义一个单词的方法,即定义一个字符序列,该序列结束于一个非字母字符。有不只一种方法可以实现它,我们将使用字符类别(也称为字符集)和范围这两个正则表达式的特性来做。字符类别即一个用方括号括起来的表达式。例如,一个匹配字符a, b, 和 c中任一个的字符类别表示为:[abc]. 如果用范围来表示同样的东西,我们要写:[a-c]. 要写一个包含所有字母的字符类型,我们可能会有点发疯,如果要把它写成: [abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ], 但不用这样;我们可以用范围来表示:[a-zA-Z]. 要注意的是,象这样使用范围要依赖于当前所用的locale,如果正则表达式的 basic_regex::collate 标志被打开。使用以上工具以及重复符 +, 它表示前面的表达式可以重复,但至少重复一次,我们现在可以表示一个单词了。

    以上正则表达式可以工作,但由于经常要表示一个单词,所以有一个更简单的方法:/w. 这个符号匹配所有单词,不仅是ASCII的单词,因此它不仅更短,而且也更适用于国际化的环境。接下来的字符是一个任意字符,我们已经知道要用点来表示。

    再接下来是 2个数字或字符串 "N/A." 为了匹配它,我们需要用到一个称为选择的特性。选择即是匹配两个或更多子表达式中的任意一个,每种选择之间用 | 分隔开。就象这样:

    注意,这个表达式被圆括号括了起来,以确保整个表达式被看作为两个选择。在正则表达式中增加一个空格是很简单的;用缩写/s. 把以上每一样东西合并起来,就得到了以下表达式:

    现在事情变得有点复杂了。我们需要某种方法,来验证接下来的输入数据中的单词是否匹配第一个单词(即那个我们用表达式[a-zA-Z]+所捕获的单词)。关键是要使用后向引用(back reference),即对前面的子表达式的引用。为了可以引用表达式 [a-zA-Z]+, 我们必须先把它用圆括号括起来。这使得表达式([a-zA-Z]+)成为我们的正则表达式中的第一个子表达式,我们就可以用索引1来建立一个后向引用了。

    这样,我们就得到了整个正则表达式,用于表示"3个数字, 一个单词, 任意字符, 2个数字或字符串"N/A," 一个空格, 然后重复第一个单词.":

    干的好!下面是一个简单的程序,把这个表达式用于算法 regex_match, 验证两个输入字符串。

    第一个字符串,123Hello N/A Hello, 是正确的;123 是3个数字,Hello 是一个后跟任意字符(一个空格)的单词, 然后是N/A和另一个空格,最后重复单词Hello 。第二个字符串是不正确的,因为单词 Hello 没有被严格重复。缺省情况下,正则表达式是大小写敏感的,因而反向引用不能匹配。

    写出正则表达式的一个关键是成功地分解问题。看一下你刚才建立的最终的那个表达式,对于未经过训练的人来说它的确很难懂。但是,如果把这个表达式分解成小的部分,它就不太复杂了。

    查找

    现在我们来看一下另一个Boost.Regex算法, regex_search. 与 regex_match 不同的是,regex_search 不要求整个输入数据完全匹配,则仅要求部分数据可以匹配。作为说明,考虑一个程序员的问题,他可能在他的程序中有一至两次忘记了调用 delete 。虽然他知道这个简单的测试可能没什么意义,他还是决定计算一下newdelete出现的次数,看看数字是否符合。这个正则表达式很简单;我们有两个选择,new 和 delete.

    boost::regex reg("(new)|(delete)");

    有两个原因我们要把子表达式用括号括起来:一个是为了表明我们的选择是两个组。另一个原因是我们想在调用regex_search时引用这些子表达式,这样我们就可以判断是哪一个选择被匹配了。我们使用regex_search的一个重载,它接受一个match_results类型的参数。当 regex_search 执行匹配时,它通过一个match_results类型的对象报告匹配的子表达式。类模板 match_results 使用一个输入序列所用的迭代器类型来参数化。

    template <class Iterator,
      class Allocator=std::allocator<sub_match<Iterator> >
        class match_results;
    
    typedef match_results<const char*> cmatch;
    typedef match_results<const wchar_t> wcmatch;
    typedef match_results<std::string::const_iterator> smatch;
    typedef match_results<std::wstring::const_iterator> wsmatch;

    我们将使用 std::string, 所以要留意 typedef smatch, 它是 match_results<std::string::const_iterator>的缩写。如果 regex_search 返回 true, 传递给该函数的 match_results 引用将包含匹配的子表达式结果。在 match_results里,用已索引的sub_match来表示正则表达式中的每个子表达式。我们来看一下我们如何帮助这位困惑的程序员来计算对newdelete的调用。

    boost::regex reg("(new)|(delete)");
    boost::smatch m;
    std::string s=
      "Calls to new must be followed by delete. /
      Calling simply new results in a leak!";
    
    if (boost::regex_search(s,m,reg)) {
      // Did new match?
      if (m[1].matched)
        std::cout << "The expression (new) matched!/n";
      if (m[2].matched)
        std::cout << "The expression (delete) matched!/n";
    }

    以上程序在输入字符串中查找 newdelete, 并报告哪一个先被找到。通过传递一个类型 smatch 的对象给 regex_search, 我们可以得知算法如何执行成功的细节。我们的表达式中有两个子表达式,因此我们可以通过match_results的索引1得到子表达式 new . 这样我们得到一个 sub_match实例,它有一个Boolean成员,matched, 告诉我们这个子表达式是否参与了匹配。因此,对于上例的输入,运行结果将输出"The expression (new) matched!/n". 现在,你还有一些工作要做。你需要继续把正则表达式应用于输入的剩余部分,为此,你要使用另外一个 regex_search的重载,它接受两个迭代器,指示出要查找的字符序列。因为 std::string 是一个容器,它提供了迭代器。现在,在每一次匹配时,你必须把指示范围起始点的迭代器更新为上一次匹配的结束点。最后,增加两个变量来记录 newdelete的次数。以下是完整的程序:

    #include <iostream>
    #include <string>
    #include "boost/regex.hpp"
    
    int main() {
      // "new" and "delete" 出现的次数是否一样?
      boost::regex reg("(new)|(delete)");
      boost::smatch m;
      std::string s=
        "Calls to new must be followed by delete. /
         Calling simply new results in a leak!";
      int new_counter=0;
      int delete_counter=0;
      std::string::const_iterator it=s.begin();
      std::string::const_iterator end=s.end();
    
      while (boost::regex_search(it,end,m,reg)) {
        // 是 new 还是 delete?
        m[1].matched ? ++new_counter : ++delete_counter;
        it=m[0].second;
      }
    
      if (new_counter!=delete_counter)
        std::cout << "Leak detected!/n";
      else
        std::cout << "Seems ok.../n";
    }

    注意,这个程序总是把迭代器 it 设置为 m[0].secondmatch_results[0] 返回对匹配整个正则表达式的子匹配的引用,因此我们可以确认这个匹配的结束点就是下次运行regex_search的起始点。运行这个程序将输出"Leak detected!", 因为这里有两次 new, 而只有一次 delete. 当然,一个变量也可能在两个地方删除,还有可能调用 new[]delete[], 等等。

    现在,你应该已经对子表达式如何分组使用有了较好的了解。现在是时候进入到最后一个Boost.Regex算法,该算法用于执行替换工作。

    替换

    Regex算法家族中的第三个算法是 regex_replace. 顾名思义,它是用于执行文本替换的。它在整个输入数据中进行搜索,查找正则表达式的所有匹配。对于表达式的每一个匹配,该算法调用 match_results::format 并输入结果到一个传入函数的输出迭代器。

    在本章的介绍部分,我给出了一个例子,将英式拼法的 colour 替换为美式拼法 color. 不使用正则表达式来进行这个拼写更改会非常乏味,也很容易出错。问题是可能存在不同的大小写,而且会有很多单词被影响,如colourize. 要正确地解决这个问题,我们需要把正则表达式分为三个子表达式。

    boost::regex reg("(Colo)(u)(r)",
      boost::regex::icase|boost::regex::perl);

    我们将要去掉的字母u独立开,为了在所有匹配中可以很容易地删掉它。另外,注意到这个正则表达式是大小写无关的,我们要把格式标志 boost::regex::icase 传给 regex 的构造函数。你还要传递你想要设置的其它标志。设置标志时一个常见的错误就是忽略了regex缺省打开的那些标志,如果你没有设置这些标志,它们不会打开,你必须设置所有你要打开的标志。

    调用 regex_replace时,我们要以参数方式提供一个格式化字符串。该格式化字符串决定如何进行替换。在这个格式化字符串中,你可以引用匹配的子表达式,这正是我们想要的。你想保留第一个和第三个匹配的子表达式,而去掉第二个(u)。表达式 $N表示匹配的子表达式, N 为子表达式索引。因此我们的格式化串应该是 "$1$3", 表示替换文本为第一个和第三个子表达式。通过引用匹配的子表达式,我们可以保留匹配文本中的所有大小写,而如果我们用字符串来作替换文本则不能做到这一点。以下是解决这个问题的完整程序。

    #include <iostream>
    #include <string>
    #include "boost/regex.hpp"
    
    int main() {
      boost::regex reg("(Colo)(u)(r)",
        boost::regex::icase|boost::regex::perl);
      
      std::string s="Colour, colours, color, colourize";
    
      s=boost::regex_replace(s,reg,"$1$3");
      std::cout << s;
    }

    程序的输出是 "Color, colors, color, colorize". regex_replace 对于这样的文本替换非常有用。

    用户常见的误解

    我所见到的与Boost.Regex相关的最常见的问题与regex_match的语义有关。人们很容易忘记必须使regex_match的所有输入匹配给定的正则表达式。因此,用户常以为以下代码会返回 true.

    boost::regex reg("//d*");
    bool b=boost::regex_match("17 is prime",reg);

    无疑这个调用永远不会得到成功的匹配。只有所有输入被 regex_match 匹配才可以返回 true!几乎所有的用户都会问为什么 regex_search 不是这样而 regex_match 是。

    boost::regex reg("//d*");
    bool b=boost::regex_search("17 is prime",reg);

    这次肯定返回 true. 值得注意的是,你可以用一些特定的缓冲操作符来让 regex_searchregex_match 那样运行。/A 匹配缓冲的起始点,而 /Z 匹配缓冲的结束点,因此如果你把 /A 放在正则表达式的开始,把 /Z 放在最后,你就可以让 regex_searchregex_match 那样使用,即必须匹配所有输入。以下正则表达式要求所有输入被匹配掉,而不管你使用的是 regex_match 或是 regex_search.

    boost::regex reg("//A//d*//Z");

    请记住,这并不表示可以无需使用 regex_match;相反,它可以清晰地表明我们刚才说到的语义,即必须匹配所有输入。

    关于重复和贪婪

    另一个容易混淆的地方是关于重复的贪婪。有些重复,如 +*,是贪婪的。即是说,它们会消耗掉尽可能多的输入。以下正则表达式并不罕见,它用于在一个贪婪的重复后捕获两个数字。

    boost::regex reg("(.*)(//d{2})");

    这个正则表达式是对的,但它可能不能匹配你想要的子表达式!表达式 .* 会吞掉所有东西而后续的子表达式将不能匹配。以下是示范这个行为的一个例子:

    int main() {
      boost::regex reg("(.*)(//d{2})");
      boost::cmatch m;
      const char* text = "Note that I'm 31 years old, not 32.";
      if(boost::regex_search(text,m, reg)) {
        if (m[1].matched)
          std::cout << "(.*) matched: " << m[1].str() << '/n';
        if (m[2].matched)
          std::cout << "Found the age: " << m[2] << '/n';
      }
    }

    在这个程序中,我们使用了match_results的另一个特化版本,即类型 cmatch. 它就是 match_results<const char*>typedef, 之所以我们必须用它而不是用之前用过的 smatch,是因为我们现在是用一个字符序列调用 regex_search 而不是用类型 std::string 来调用。你期望这个程序的运行结果是什么?通常,一个刚开始使用正则表达式的用户会首先想到 m[1].matchedm[2].matched 都为 true, 且第二个子表达式的结果会是 "31". 接着在认识到贪婪的重复所带来的效果后,即重复会尽可能消耗输入,用户会想到只有第一个子表达式是 true,即 .* 成功地吞掉了所有的输入。最后,新用户得到了下结论,两个子表达式都被匹配,但第二个表达式匹配的是最后一个可能的序列。即第一个子表达式匹配的是 "Note that I'm 31 years old, not" 而第二个匹配 "32".

    那么,如果你想使用重复并匹配另一个子表达式的第一次出现,该怎么办?要使用非贪婪的重复。在重复符后加一个 ? ,重复就变为非贪婪的了。这意味着该表达式会尝试发现最短的匹配可能而不再阻止表达式的剩余部分进行匹配。因此,要让前面的正则表达式正确工作,我们需要把它改为这样。

    boost::regex reg("(.*?)(//d{2})");

    如果我们用这个正则表达式来修改程序,那么 m[1].matchedm[2].matched 都会为 true. 表达式 .*? 只消耗最少可能的输入,即它将在第一个字符 3处停止,因为这就是表达式要成功匹配所需要的。因此,第一个子表达式会匹配 "Note that I'm" 而第二个匹配 "31".

    看一下 regex_iterator

    我们已经看过如何用几次 regex_search 调用来处理所有输入,但另一方面,更为优雅的方法是使用 regex_iterator. 这个迭代器类型用一个序列来列举正则表达式的所有匹配。解引用一个 regex_iterator 会产生对一个 match_results 实例的引用。构造一个 regex_iterator 时,你要把指示输入序列的迭代器传给它,并提供相应的正则表达式。我们来看一个例子,输入数据是一组由逗号分隔的整数。相应的正则表达式很简单。

    boost::regex reg("(//d+),?");

    在正则表达式的最后加一个 ? (匹配零次或一次) 确保最后一个数字可以被成功分析,即使输入序列不是以逗号结束。另外,我们还使用了另一个重复符 +. 这个重复符表示匹配一次或多次。现在,不需要多次调用 regex_search, 我们创建一个 regex_iterator, 并调用算法 for_each, 传给它一个函数对象,该函数对象以迭代器的解引用进行调用。下面是一个接受任意形式的match_results的函数对象,它有一个泛型的调用操作符。它所执行的就是把当前匹配的值加到一个总和中(在我们的正则表达式中,第一个子表达式是我们要用的)。

    class regex_callback {
      int sum_;
    public:
      regex_callback() : sum_(0) {}
    
      template <typename T> void operator()(const T& what) {
        sum_+=atoi(what[1].str().c_str());
      }
    
      int sum() const {
        return sum_;
      }
    };

    现在把这个函数对象的一个实例传递给 std::for_each, 结果是对每一个迭代器it的解引用调用该函数对象,即对每一次匹配的子表达式进行调用。

    int main() {
      boost::regex reg("(//d+),?");
      std::string s="1,1,2,3,5,8,13,21";
    
      boost::sregex_iterator it(s.begin(),s.end(),reg);
      boost::sregex_iterator end;
    
      regex_callback c;
      int sum=for_each(it,end,c).sum();
    }

    如你所见,传递给for_each的end迭代器是 regex_iterator 一个缺省构造实例。itend 的类型均为 boost::sregex_iterator, 即为 regex_iterator<std::string::const_iterator>typedef. 这种使用 regex_iterator 的方法要比我们前面试过的多次匹配的方法更清晰,在多次匹配的方法中我们不得不在一个循环中让起始迭代器不断地前进并调用 regex_search

    regex_token_iterator 分割字符串

    另一个迭代器类型,或者说得更准确些,迭代器适配器,就是 boost::regex_token_iterator. 它与 regex_iterator 很类似,但却是用于列举不匹配某个正则表达式的每一个字符序列,这对于分割字符串很有用。它也可以用于选择对哪一个子表达式感兴趣,当解引用 regex_token_iterator时,只有预订的那个子表达式被返回。考虑这样一个应用程序,它接受一些用斜线号分隔的数据项作为输入。两个斜线号之间的数据组成应用程序要处理的项。使用 regex_token_iterator来分割这个字符串很容易。该正则表达式很简单。

    boost::regex reg("/");

    这个 regex 匹配各项间的分割符。要用它来分割输入,只需简单地把指定的索引 1 传递给 regex_token_iterator 的构造函数。以下是完整的程序:

    int main() {
      boost::regex reg("/");
      std::string s="Split/Values/Separated/By/Slashes,";
      std::vector<std::string> vec;
      boost::sregex_token_iterator it(s.begin(),s.end(),reg,-1);
      boost::sregex_token_iterator end;
      while (it!=end) 
        vec.push_back(*it++);
    
      assert(vec.size()==std::count(s.begin(),s.end(),'/')+1);
      assert(vec[0]=="Split");
    }

    就象 regex_iterator 一样,regex_token_iterator 是一个模板类,它使用所包装的序列的迭代器类型来进行特化。这里,我们用的是 sregex_token_iterator, 它是 regex_token_iterator<std::string::const_iterator>typedef 。每一次解引用这个迭代器it,它返回当前的 sub_match, 当这个迭代器前进时,它尝试再次匹配该正则表达式。这两个迭代器类型,regex_iteratorregex_token_iterator, 都非常有用;你应该明白,当你考虑反复调用regex_search时,就该用它们了。

    更多的正则表达式

    你已经看到了不少正则表达式的语法,但还有更多的要了解。这一节简单地示范一些你每天都会使用的正则表达式的其它功能。作为开始,我们先看一下一组完整的重复符;我们之前已经看到了 *, +, 以及使用 {} 进行限定重复。还有一个重复符,即是 ?. 你可能已经留意到它也可以用于声明非贪婪的重复,但对于它本身而言,它是表示一个表达式必须出现零次或一次。还有一点值得提及的是,限定重复符可以很灵活;下面是三种不同的用法:

    boost::regex reg1("//d{5}");
    boost::regex reg2("//d{2,4}");
    boost::regex reg3("//d{2,}");

    第一个正则表达式匹配5个数字。第二个匹配 2个, 3个, 或者 4个数字。第三个匹配2个或更多个数字,没有上限。

    另一种重要的正则表达式特性是使用元字符 ^ 表示非字符类别。用它来表示一个匹配任意不在给定字符类别中的字符;即你所列字符类别的补集。例如,看如下正则表达式。

    boost::regex reg("[^13579]");

    它包含一个非字符类别,匹配任意不是奇数数字的字符。看一下以下这个小程序,试着给出程序的输出。

    int main() {
      boost::regex reg4("[^13579]");
      std::string s="0123456789";
      boost::sregex_iterator it(s.begin(),s.end(),reg4);
      boost::sregex_iterator end;
    
      while (it!=end) 
        std::cout << *it++;
    }

    你给出答案了吗?输出是 "02468",即所有偶数数字。注意,这个字符类别不仅匹配偶数数字,如果输入字符串是 "AlfaBetaGamma",那么也会全部匹配。

    我们看到的这个元字符, ^, 还有另一个意思。它可以用来表示一行的开始。而元字符 $ 则表示一行的结束。

    错的正则表达式

    一个错的正则表达式就是一个不遵守规则的正则表达式。例如,你可能忘了一个右括号,这样正则表达式引擎将无法成功编译这个正则表达式。这时,将抛出一个 bad_expression 类型的异常。正如我前面提到的,这个异常的名字将会在下一版本的Boost.Regex中被修改,还有在即将加入Library Technical Report的版本中也是。异常类型 bad_expression 将被更名为 regex_error.

    如果你的应用程序中的正则表达式全都是硬编码的,你可能不用处理错误表达式,但如果你是接受了用户的输入来作为正则表达式,你就必须准备进行错误处理。这里有一个程序,提示用户输入一个正则表达式,接着输入一个用来对正则表达式进行匹配的字符串。由用户进行输入时,总是有可能会导致无效的输入。

    int main() {  
      std::cout << "Enter a regular expression:/n";
      std::string s;
      std::getline(std::cin, s);
      try {
        boost::regex reg(s);
        std::cout << "Enter a string to be matched:/n";
     
        std::getline(std::cin,s);
     
        if (boost::regex_match(s,reg))
          std::cout << "That's right!/n";
        else
          std::cout << "No, sorry, that doesn't match./n";
      }
      catch(const boost::bad_expression& e) {
        std::cout << 
          "That's not a valid regular expression! (Error: " << 
          e.what() << ") Exiting.../n";
      }
    }

    为了保护应用程序和用户,一个 try/catch 块用于处理构造时抛出 boost::regex 的情形,这时会打印一个提示信息,而程序会温和地退出。用这个程序来测试,我们开始时输入一些合理的数据。

    Enter a regular expression:
    /d{5}
    Enter a string to be matched:
    12345
    That's right!

    现在,给一些错误的数据,试着输入一个错误的正则表达式。

    Enter a regular expression:
    (/w*))
    That's not a valid regular expression! (Error: Unmatched ( or /() Exiting...

    regex reg构造时,就会抛出一个异常,因为这个正则表达式不能被编译。因此,进入到 catch 的处理例程中,程序将打印一个错误信息并退出。你只需知道有三个可能会发生异常的地方。一个是在构造一个正则表达式时,就象你刚刚看到的那样;另一个是使用成员函数 assign 把正则表达式赋给一个 regex 时。最后一个是,regex迭代器和算法也可能抛出异常,如果内存不够或者匹配的复杂度过快增长的话。

     

    Regex 总结

    无可争议,正则表达式是非常有用和重要的,而本库给C++带来了强大的正则表达式功能。传统上,用户除了使用POSIX C API来实现正则表达式功能以外,别无选择。对于文本处理的验证工作,正则表达式比手工编写分析代码要灵活和可靠得多。对于查找和替换,使用正则表达式可以优美地解决很多相关问题,而不用它们则根本无法解决。

    Boost.Regex是一个强大的库,因此不可能在这一章中完全覆盖它所有的内容。同样,正则表达式的完美表现和广泛的应用范围意味着本章也不仅仅是简单地介绍一下它们。这个主题可以写成一本单独的书。要知道更多,可以学习Boost.Regex的在线文档,并且找一本关于正则表达式的书(考虑一下参考书目中的建议)。不论Boost.Regex有多强大,正则表达式有多广多深,初学者还是可以有效地使用本库中的正则表达式。对于那些由于C++不支持正则表达式而选择了其它语言的程序员,欢迎你们回家。

    Boost.Regex并不是C++程序员唯一可以使用的正则表达式库,但它的确是最好的一个。它易于使用,并且在匹配你的正则表达式时快如闪电。你应该尽可能去用它。

    Boost.Regex 的作者是 Dr. John Maddock.

    展开全文
  • 1.匹配单个字符 . 匹配任意单个字符(除\n) [] 列举,匹配[] 中列举的内容 [ab] 匹配 a 或者 b [a-z] 匹配所有的小写字母 [A-Z] 匹配大写字母 ...[a-zA-Z] 匹配所有的小写字母和大写字母 ...\w...

    1.匹配单个字符

    • . 匹配任意单个字符(除\n)

    • [] 列举,匹配[] 中列举的内容

      [ab] 匹配 a 或者 b

      [a-z] 匹配所有的小写字母

      [A-Z] 匹配大写字母

      [0-9] 匹配数字

      [a-zA-Z] 匹配所有的小写字母和大写字母

    • \d 匹配所有的数字 等价于 [0-9]

    • \D 非数字

    • \s 空格

    • \S 非空格

    • \w 匹配 字母、数字、下划线

      [a-zA-Z0-9_]

    • \W 非数字、非字母、非下划线

    2.匹配多个字符

    • *表示前一个字符出现 0次或者 无限次

    • + 表示 前一个字符出现 1次或者 无限次

    • ?表示 前一个字符出现 0 次或者 1次 (要不不出现,要不只能出现一次)

    • {m} 表示前一个字符,连续出现 m次

    • {m,n} 表示前一个字符,连续出现最少m次,最多n次

      m 一定要小于 n

    3.匹配开头结尾

    • ^ 表示 匹配 以后一个字符开头

      ^ 有两个作用:

      1)匹配以 指定字符开头

      ^[a-zA-Z_]+\w   # 必须以 小写字母、大写字母、下花线开头
      

      2)用在 [] 内部,用于取反

      [^he] 匹配不含有 h 和 e 的字符
      
    • $表示匹配 以前一个字符结尾

      \d$ 以数字结尾

    4.re模块操作

    • re模块的作用: python提供的用于正则操作的模块

    • re模块的使用步骤:

      • 导入模块

        import re

      • 使用match() 方法进行检测

        # 2、通过 match 方法,验证正则
        # re.match("正则表达式", "要验证/检测的字符串")
        # match() 方法如果匹配成功,返回 match object 对象
        # match() 方法如果匹配失败,返回 None
        #        正则字符串    要检测的内容
        result = re.match("\w{4,20}@163\.com$", "hello@163.com")
        
      • 判断是否检测/匹配成功

        if result:

        ​ print(“成功”)

        else:

        ​ print(“失败”)

      • 取出匹配的具体内容

        result.group() 获取匹配的具体内容

    5.匹配分组之"|"

    • | 的作用:或者关系,多个正则表达式满足任意一个都可以

      ^[0-9]?[0-9]$|^100$   # ^[0-9]?[0-9]$ 满足或者 ^100$ 满足任意一个,返回值都是一个
      match object 对象(匹配成功)
      

    6.匹配分组之"()"

    • 分组,整体匹配

      result = re.match("\w{4,20}@(163|126|qq|sina)\.com$", "hello@126.com")
      

      把 @ … “.com” 之间的内容整体进行匹配

    • 提取子字符串

      ​ 1 2

      result = re.match("(\d{3,4})-(\d{7,8})", “010-12345678”)`

      result.group() 获取的是匹配的结果

      result.group(1) 获取的是第一个括号中的内容

      result.group(2) 获取的是第二个括号中的内容

    7.匹配分组之‘\’

    • 引用 分组

      \1 表示引用第一组

      # result = re.match("<([a-zA-Z0-9]+)>.*</\\1>", "<html>asdbasldfj</html>")
      result = re.match("<([a-zA-Z0-9]+)><([a-zA-Z0-9]+)>.*</\\2></\\1>", "<html><h1>asdbj</h1></html>")
      

      \\1 表示引用第一组 ,\\是转义字符,转义后代表一个 \

      \\2 表示引用第二组

    • 分组起别名

      • 起名

        ?P 给分组起别名,别名为name1

      • 引用别名

        ?P=name1 引用别名为 name1的分组

      • 整体代码:

        result = re.match("<(?P<name1>[a-zA-Z0-9]+)><(?P<name2>[a-zA-Z0-9]+)>.*</(?P=name2)></(?P=name1)>", "<html><h1>asdbj</h1></html>")
        

    8.re模块的高级用法

    • search() 在需要匹配的字符串中搜索要匹配的内容

      result = re.search("\d+", “阅读次数:9999”)

      知道 match 和 search的区别

      # 1)match 从需要检测.group的字符串的开头位置匹配,如果失败返回 None
      # 2)search 从需要检测的字符串中搜索满足正则的内容,有则返回match object对象
      
    • findall() 在需要匹配的字符串中查找所有满足正则的内容,返回值是列表

      result = re.findall("\d+", “阅读次数:9999,转发次数:6666,评论次数:38”)

    • sub(“正则表达式”, “新的内容”, “要替换的字符串”) 字符串替换(按照正则,查找字符串并且替换为指定的内容)返回值是替换后的字符串

      result = re.sub("\d+", “10000”, “阅读次数:9999,转发次数:6666,评论次数:38”)

    • split(“正则表达式”, “待拆分的字符串”) 按照正则拆分字符串,返回值是一个列表

      result = re.split("? “, "info:hello@163.com zhangsan lisi”)

    9.贪婪和非贪婪

    • 贪婪:默认,表示在满足正则的情况尽可能多的取内容
    • 非贪婪:表示在满足正则的情况下,尽可能少的取内容
    • 贪婪的转变为非贪婪: 在 * ? + {} 的后面再加上 ?就可以了

    result = re.match(“aaa(\d+?)”, “aaa123456”)

    10.r的作用

    • r的作用: 让正则中的 \ 表示原生的含义,仅仅对 \起作用

    11.案例:《简单爬虫-批量获取电影下载链接》

    • 思路:

      一、定义函数获取列表页的内容页地址 get_movie_links()
      1、定义列表的地址 http://www.ygdy8.net/html/gndy/dyzz/list_23_1.html
      2、打开url地址,获取数据
      3、解码获取到的数据
      4、使用正则得到所有的影片内容也地址

      ​ 4.1 遍历,取出内容页地址

      ​ 4.2 拼接内容页地址

      ​ 4.3 打开内容页地址

      ​ 4.4 获取数据,并读取

      ​ 4.5 解码内容页数据,得到html内容页文本

      ​ 4.6 使用正则,获取下载地址的连接

      ​ 4.7 把影片信息和下载链接,保存到字典中

      ​ 4.8 返回字典

      二、主函数 main

      1、调用 get_movie_lisgt() ,得到字典

      2、遍历字典,显示下载的内容

    展开全文
  • c++11之regex:初识regex

    千次阅读 2016-06-26 14:02:32
    c++11之regex:初识regexC++11开始支持正则表达式了,使得文本更加简单高效。 C++11支持以下六种正则表达式语法 ECMAScript most powerful basic(POSIX Basic Regular Expressions) extended(POSIX Extended ...
  • C#语言的正则表达式(Regex)详解

    千次阅读 2017-11-22 00:00:57
    正则表达式我们并不陌生,比如需要验证用户输入是否合法,比如输入邮箱的输入框判断输入是否合乎要求等等。 简单来说,正则表达式就是定义好固定的格式,再对用户的输入信息验证。以判断输入是否合法。...
  • 浅谈C++的regex

    千次阅读 2019-04-04 16:02:39
    写在前面: 第四届CCCC团体程序设计天梯赛结束之后,知乎热榜出现了这个问题“如何评价第四届CCCC团体程序设计天梯赛”,这里面有些回答真的看得我笑出声,比如这个简简单单的回答:“第四届CCCC字符串大赛”。...
  • hive 正则表达式详解

    万次阅读 2016-04-09 18:56:22
    hive中的正则表达式还是很强大的。数据工作者平时也离不开正则表达式。对此,特意做了个hive正则表达式的小结。所有代码都经过亲测,正常运行。1.regexp语法: A REGEXP B 操作类型: strings 描述: 功能与RLIKE相同...
  • Java —— 正则表达式 Regex

    万次阅读 2017-08-22 12:07:07
    Java正则表达式的语法与示例 java 正则表达式 语法 示例 概要: Java正则表达式的语法与示例 | |目录 1匹配验证-验证Email是否正确 2在字符串中查询字符或者字符串 3常用正则表达式 4正则表达式语法 ...
  • C语言正则表达式RegEx

    千次阅读 2017-10-24 11:45:11
    说明: #Regular Expression Overview . (dot) - a single character. ... - the preceding character matches 0 or 1 times only. ...* - the preceding character matches 0 or more times. ...+ - the preceding ch
  • 正则表达式查找文本模式字符串中查找电话号码。只知道模式: 3个数字,一个短横线,4个数字,一个短横线,再是4个数字。例如:010-8888-8888,或010.8888.8888或(010) 8888-8888 还有分机010-8888-8888 转456 该怎么办...
  • Boost.Regex 正则表达式是解决大量模式匹配问题的基础。它们常用于处理大的字符串,子串模糊查找,按某种...Regex提供了高效和强大的正则表达式支持,基于与STL同样的前提而设计,这使得它很容易使用。Regex已被即
  • C++正则表达式regex库使用方法总结

    千次阅读 2019-12-06 11:54:02
    目录 一、regex库中常用组件介绍 二、regex库中常用组件使用 1、regex库组件使用准备 2、regex_match使用 ...1、C++正则表达式接口类设计 2、C++正则表达式接口类使用 四、部分语法规则介绍 正...
  • Mongodb正则表达式$regex操作符

    万次阅读 2017-12-14 11:12:16
    一、$regex为模糊查询的字符串提供正则表达式功能,MongoDB使用Perl兼容正则表达式(即“文件”)8.41版与UTF-8支持。 使用$regex操作符要使用如下语法: { : { $regex: /pattern/, $options: '' } } { : { $regex: ...
  •  一、正则表达式正则表达式(regular expression,或简称regex ),是一种字符串查找的语言,一个正则表达式,就是用某种模式去匹配一类字符串的一个公式。举几个例子吧: 正则表达式 "r.t",其中 . 表示任意一个...
  • C++ regex 正则表达式的使用

    万次阅读 多人点赞 2014-02-09 19:32:01
    在c++中,有三种正则可以选择使用,C ++regex,C regex,boost regex ,如果在windows下开发c++,默认不支持后面两种正则,如果想快速应用,显然C++ regex 比较方便使用。文章将讨论C++ regex 正则表达式的使用。
  • 正则表达式Regex

    千次阅读 2017-01-25 11:53:21
    1.概念正则表达式,又称规则表达式。(英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。正则表通常被用来检索、替换那些符合某个模式(规则)的文本。 正则表达式是对字符串...
  • c++11 <regex>正则表达式匹配

    万次阅读 2016-05-11 16:27:10
        #include #include #include //标准 using namespace std; int main() {  string str("This expression could match from A and beyond. [ expression express ... strin
  • C++ regex库的三种正则表达式操作

    千次阅读 2018-05-18 11:08:51
    上官栋博客园首页新随笔联系订阅管理C++ regex库的三种正则表达式操作关于正则表达式的语法和字符含义,网上已经有很不错的博客教学,我当初参考的是读懂正则表达式就这么简单 - Zery - 博客...– 语法 | 菜鸟教程 ...
  • 正则表达式测试工具 中文版 非常的好用 希望各位学友下载使用
  • 提取了boost::regex里边的几个正则表达式匹配搜索接口,打包成DLL,以在不依赖于boost的情况下独立编译运行
  • 正则表达式(regex) 替换(replace) 的 详解 及 代码 本文地址:http://blog.csdn.net/caroline_wendy/article/details/17321639 正则表达式, 可以替换(replace)匹配的字符串, 使用regex_replace()函数, 需要指定...
  • 正则表达式测试工具“RegexTester”,下载地址:http://www.oschina.net/p/regex+tester 一、关于本文 今天的工作中遇到了一些正则表达式,我需要检验它们是否正确,不过我对自己目测的结果又没有信心。后来我...
  • c++ regex 正则表达式使用

    千次阅读 2018-12-27 16:08:56
    文章目录概述正则表达式库例子 概述 正则表达式是在c++11之后才被支持,叫正则表达式库。 使用的时候要包含&lt;regex&gt;头文件 正则表达式正则表达式有3个主要的算法 regex_match regex_search ...
  • 在用c语言写爬虫(入坑),想用正则表达式来判断url结果发现复杂的判断(在其他正则表达式测试工具可以正常判断出来)没法判断出来,换成简单的测试了下(代码如下匹配是简单的)可以匹配出来,复杂的:![图片说明]...
  • 正则表达式是一个很强的工具,可以在你的软件中增强查找、替换、匹配等功能。附件中封装了Henry Spencer的regex library源码,有说明和范例,纯C可移值,简单易用。
  • 标准的C/C++库不支持正则表达式。在Posix函数库中包含了<regex.h>正则表达式库。 ## 正则表达式匹配框架 标准的正则表达式匹配框架: 编译正则表达式. 匹配正则表达式. 释放正则表达式. 编译正则表达式 #...
  • C++正则表达式regex

    2019-10-20 16:38:16
    一、作用 可用于字符串的匹配(Match)、查找(Search)、切割...二、如何使用正则表达式库 1、引入头文件#include <regex>; 2、指定匹配模式std::regex; 3、调用正则函数std::regex_macth/search/repl...
  • 这篇文章是个人对c++正则表达式程序库regex使用的笔记,如有不正确的地方欢迎指正。 Regex库简介 Regex是从c++ 11开始有的。 c++正则表达式提供了以下几个主要功能: Math:将整个输入与正则表达式进行比对。 ...
  • C语言中巧用正则表达式 regex_t

    千次阅读 2013-06-20 13:49:46
    标准的C和C++都不支持正则表达式,但有一些函数库可以辅助C/C++程序员完成这一功能,其中最著名的当数Philip Hazel的Perl-Compatible Regular Expression库,许多Linux发行版本都带有这个函数库。 C语言处理正则...

空空如也

1 2 3 4 5 ... 20
收藏数 709,991
精华内容 283,996
关键字:

regex