正则表达式在 g++ 4.9 下匹配,但在 g++-5.3.1 下失败
Posted
技术标签:
【中文标题】正则表达式在 g++ 4.9 下匹配,但在 g++-5.3.1 下失败【英文标题】:Regex matches under g++ 4.9 but fails under g++-5.3.1 【发布时间】:2016-05-10 19:41:15 【问题描述】:我正在用正则表达式标记一个字符串;这在g++-4.9
下正常工作,但在g++-5.3.1
下失败。
我有以下 txt 文件:
0001-SCAND ==> "Scandaroon" (from Philjumba)
0002-KINVIN ==> "King's Vineyard" (from Philjumba)
0003-HANNI ==> "Hannibal: Rome vs. Carthage" (from Philjumba)
0004-LOX ==> "Lords of Xidit" (from Philjumba)
我使用正则表达式、空格、引号对和括号对进行标记。例如,第一行应该标记如下:
0001-SCAND
==>
"Scandaroon"
(from Philjumba)
我写了以下std::regex
:
std::regex FPAT("(\\S+)|(\"[^\"]*\")|(\\([^\\)]+\\))";
我正在使用以下方法标记字符串:
std::vector<std::string>
split( const std::string & input, const std::regex & regex )
std::sregex_token_iterator
firstinput.begin(), input.end(), regex, 0,
last;
return first, last;
这将返回匹配项。在g++-4.9
下,字符串按请求进行标记化,但在g++-5.3.1
下,它的标记化如下:
0001-SCAND
==>
"Scandaroon"
(from
Philjumba)
或者第三行被分词如下:
0003-HANNI
==>
"Hannibal:
Rome
vs.
Carthage"
(from
Philjumba)
可能是什么问题?
编辑:我调用函数如下:
std::string line("0001-SCAND ==> \"Scandaroon\" (from Philjumba)");
auto elems = split( line, FPAT );
编辑:根据@xaxxon 的反馈,我将返回迭代器替换为向量,但在g++-5.3
下仍然无法正常工作。
std::vector<std::string>
split( const std::string & input, const std::regex & regex )
std::sregex_token_iterator
firstinput.begin(), input.end(), regex, 0,
last;
std::vector< std::string > elems;
elems.reserve( std::distance(first,last) );
for ( auto it = first; it != last; ++ it )
//std::cout << (*it) << std::endl;
elems.push_back( *it );
return elems;
【问题讨论】:
顺便说一句,原始字符串可能有助于避免额外的转义:R"((\S+)|("[^"]*")|(\([^\)]+\)))"
。
@Jarod42 谢谢,现在应该修复了。显然,在复制/粘贴期间添加了一个额外的)
。
请发布您是如何调用此函数的——如果您使用右值作为第一个参数调用它,则在您使用迭代器时该字符串可能无效。幕后的实际内存分配很容易在编译器版本之间发生变化,导致在旧版本中看起来还不错的内存在新版本中看起来不太好。
regex101.com/#pcre、R"(("[^\"]*\")|(\([^\)])+\)|(\S+))"
可以正常工作,但R"((\S+)|("[^\"]*\")|(\([^\)])+\))"
不能...(\S+
在末尾或开头)。
@xaxxon 已添加,感谢您的反馈。如果需要更多信息,请告诉我。
【参考方案1】:
正则表达式是Eager
所以对于正则表达式"Set|SetValue"
和文本"SetValue"
,正则表达式创建"Set"
。
您必须谨慎选择订单:
std::regex FPAT(R"(("[^\"]*\")|(\([^\)])+\)|(\S+))");
\S+
最后被考虑。
另一种选择是不使用默认选项(请参阅http://en.cppreference.com/w/cpp/regex/syntax_option_type)
并使用std::::regex::extended
std::regex FPAT(R"((\S+)|("[^\"]*\")|(\([^\)])+\))", std::::regex::extended);
看来g++-5.3.1在这方面自g++-4.9以来修复了一个bug。
【讨论】:
是的,这确实是问题所在!事实上,几天前我了解到正则表达式是“渴望”并修复了一个类似的错误,但忘记了这个。谢谢!【参考方案2】:您发布的内容不足以让我确定(您更新它显示您正在使用左值调用它,所以这篇文章可能不相关,但除非人们希望我接受,否则我会留下它它下来),但如果你正在做我所做的,你忘记了迭代器在源字符串中并且该字符串不再有效。
您可以从input
中删除const
,但是能够在其中放置右值非常方便,所以.....
这是我为避免这种情况所做的 - 我将 unique_ptr 返回到看起来像结果的东西,但我将实际的源字符串连同它一起隐藏起来,因此在我用完它之前 strsing 不会消失。这可能是 UB,但我认为它几乎可以一直工作:
// Holds a regex match as well as the original source string so the matches remain valid as long as the
// caller holds on to this object - but it acts just like a std::smatch
struct MagicSmatch
std::smatch match;
std::string data;
// constructor makes a copy of the string and associates
// the copy's lifetime with the iterators into the string (the smatch)
MagicSmatch(const std::string & data) : data(data)
;
// this deleter knows about the hidden string and makes sure to delete it
// this cast is probably UB because std::smatch isn't a standard layout type
struct MagicSmatchDeleter
void operator()(std::smatch * smatch)
delete reinterpret_cast<MagicSmatch *>(smatch);
;
// the caller just thinks they're getting a smatch ptr.. but we know the secret
std::unique_ptr<std::smatch, MagicSmatchDeleter> regexer(const std::regex & regex, const std::string & source)
auto magic_smatch = new MagicSmatch(source);
std::regex_search(magic_smatch->data, magic_smatch->match, regex);
return std::unique_ptr<std::smatch, MagicSmatchDeleter>(reinterpret_cast<std::smatch *>(magic_smatch));
只要你把它称为 auto results = regexer(....) 那么它就很容易使用,虽然 results
是一个指针,而不是正确的 smatch
,所以 []
语法没有'效果也不错。
【讨论】:
以上是关于正则表达式在 g++ 4.9 下匹配,但在 g++-5.3.1 下失败的主要内容,如果未能解决你的问题,请参考以下文章