将临时std::string传递给boost::regex_match

Passing temporary std::string to boost::regex_match

本文关键字:boost regex match std string      更新时间:2023-10-16

我想将一串整数对解析为数字。我使用以下代码:

#include <iostream>
#include <boost/regex.hpp>
int main()
{
    boost::regex reg( "(\d+):(\d+)" );
    std::string test = "1:2 3:4 5:6";
    boost::sregex_token_iterator end;
    for( boost::sregex_token_iterator i( test.begin(), test.end(), reg ); i != end; ++i ) {
        boost::smatch what;
        if( boost::regex_match( i->str(), what, reg ) )
            std::cout << "found: "" << what[1].str() << "":"" << what[2].str() << """ << std::endl;
    }
    return 0;
}
预期输出:

found: "1":"2"
found: "3":"4"
found: "5":"6"

使用gcc 4.7.2编译的boost 1.52的结果:

found: "2":"2"
found: "4":"4"
found: "6":"6"

boost 1.52 clang 3.2

found: "":"2"
found: "":"4"
found: "":"6"

我的代码有什么问题?

感谢Fraser的提示,一个可能的解决方案是:

for( boost::sregex_token_iterator i( test.begin(), test.end(), reg ); i != end; ++i ) {
    boost::smatch what;
    const std::string &str = i->str(); 
    if( boost::regex_match( str, what, reg ) )
       std::cout << "found: "" << what[1].str() << "":"" << what[2].str() << """ << std::endl;
}

问题在于i->str()调用boost::sub_match方法:

basic_string<value_type> str()const;

,它按值返回std::string。因此,传递给regex_match和boost::smatch对象的std::string的临时对象实际上会记住原始字符串中的位置,而在boost::regex_match完成后,原始字符串实际上会被销毁。类似的问题可以再现如下:

std::string function();
boost::smatch what;
if( boost::regex_match( function(), what, reg ) ) ...

或者我认为这样的代码也是脆弱的:

boost::smatch what;
if( boost::regex_match( std::string( "abc" ), what, reg ) ) ...

我不确定如何在编译时防止这种情况,是否应该将其视为错误。Std::regex_match似乎有相同的签名,是否存在这个问题?

我不确定Boost的实现细节。Regex,但似乎将for循环内的解引用sregex_token_iterator复制到临时std::string解决了这个问题:

std::string copied( i->str() );
boost::smatch what;
if( boost::regex_match( copied, what, reg ) ) {
    std::cout << "found: "" << what[1].str() << "":"" << what[2].str() << """ << std::endl;
}

希望有人对Boost有更好的了解。

我不知道Boost现在有什么细节,但我认为它不会影响这一点。我也不知道为什么你在调用regex_match之后得到了奇怪的结果,但这是不需要的;token_iterator已经完成了匹配,所以你只需要

std::cout << (*i)[1].str() << ':' << (*i)[2].str() << std::endl;

或者,如果你喜欢:

std::cout << i->str(1) << ':' << i->str(2) << std::endl;

注意这是c++ 11。它也应该与Boost一起工作,但我还没有尝试过。