Nic*_*s M 6 c++ regex string c-preprocessor
我目前正在研究C++预处理器,我需要将字符串常量与超过0个字母匹配"hey I'm a string.我目前正在使用这个,\"([^\\\"]+|\\.)+\"但是我的一个测试用例失败了.
测试用例:
std::cout << "hello" << " world";
std::cout << "He said: \"bananas\"" << "...";
std::cout << "";
std::cout << "\x12\23\x34";
Run Code Online (Sandbox Code Playgroud)
预期产量:
std::cout << String("hello") << String(" world");
std::cout << String("He said: \"bananas\"") << String("...");
std::cout << "";
std::cout << String("\x12\23\x34");
Run Code Online (Sandbox Code Playgroud)
在第二个我反而得到
std::cout << String("He said: \")bananas\"String(" << ")...";
Run Code Online (Sandbox Code Playgroud)
短复制代码(使用AR.3的正则表达式):
std::string in_line = "std::cout << \"He said: \\\"bananas\\\"\" << \"...\";";
std::regex r("\"([^\"]+|\\.|(?<=\\\\)\")+\"");
in_line = std::regex_replace(in_line, r, "String($&)");
Run Code Online (Sandbox Code Playgroud)
对源文件进行词法分析对于正则表达式来说是一项很好的工作。但是对于这样的任务,让我们使用比std::regex. 让我们首先使用 PCRE(或boost::regex)。在这篇文章的最后,我将展示您可以使用功能较少的引擎做什么。
我们只需要进行部分词法分析,忽略所有不会影响字符串文字的无法识别的标记。我们需要处理的是:
我们将使用扩展 ( x) 选项,它会忽略模式中的空格。
内容如下[lex.comment]:
字符
/*开始注释,以字符 结束*/。这些注释不嵌套。字符//开始注释,在下一个换行符之前立即终止。如果这样的注释中有换页符或垂直制表符,则在它和终止注释的换行符之间只能出现空白字符;不需要诊断。[注意:注释字符//,/*, 和*/在//注释中没有特殊含义,与其他字符一样对待。同样,注释字符//和注释在注释中/*也没有特殊含义/*。— 尾注 ]
# singleline comment
// .* (*SKIP)(*FAIL)
# multiline comment
| /\* (?s: .*? ) \*/ (*SKIP)(*FAIL)
Run Code Online (Sandbox Code Playgroud)
十分简单。如果你在那里匹配任何东西,只是(*SKIP)(*FAIL)- 意味着你扔掉匹配。在(?s: .*? )该应用s(单线),修饰的.元字符,这意味着它允许匹配换行符。
以下是语法[lex.ccon]:
Run Code Online (Sandbox Code Playgroud)character-literal: encoding-prefix(opt) ’ c-char-sequence ’ encoding-prefix: one of u8 u U L c-char-sequence: c-char c-char-sequence c-char c-char: any member of the source character set except the single-quote ’, backslash \, or new-line character escape-sequence universal-character-name escape-sequence: simple-escape-sequence octal-escape-sequence hexadecimal-escape-sequence simple-escape-sequence: one of \’ \" \? \\ \a \b \f \n \r \t \v octal-escape-sequence: \ octal-digit \ octal-digit octal-digit \ octal-digit octal-digit octal-digit hexadecimal-escape-sequence: \x hexadecimal-digit hexadecimal-escape-sequence hexadecimal-digit
让我们先定义一些东西,稍后我们将需要它们:
(?(DEFINE)
(?<prefix> (?:u8?|U|L)? )
(?<escape> \\ (?:
['"?\\abfnrtv] # simple escape
| [0-7]{1,3} # octal escape
| x [0-9a-fA-F]{1,2} # hex escape
| u [0-9a-fA-F]{4} # universal character name
| U [0-9a-fA-F]{8} # universal character name
))
)
Run Code Online (Sandbox Code Playgroud)
prefix被定义为一个可选的u8, u,U或Lescape是按照标准定义的,只是universal-character-name为了简单起见我已经合并了一旦我们有了这些,字符文字就非常简单了:
(?&prefix) ' (?> (?&escape) | [^'\\\r\n]+ )+ ' (*SKIP)(*FAIL)
Run Code Online (Sandbox Code Playgroud)
我们把它扔掉 (*SKIP)(*FAIL)
它们的定义方式几乎与字符文字相同。这是其中的一部分[lex.string]:
Run Code Online (Sandbox Code Playgroud)string-literal: encoding-prefix(opt) " s-char-sequence(opt) " encoding-prefix(opt) R raw-string s-char-sequence: s-char s-char-sequence s-char s-char: any member of the source character set except the double-quote ", backslash \, or new-line character escape-sequence universal-character-name
这将反映字符文字:
(?&prefix) " (?> (?&escape) | [^"\\\r\n]+ )* "
Run Code Online (Sandbox Code Playgroud)
区别在于:
*而不是+)这是原始字符串部分:
Run Code Online (Sandbox Code Playgroud)raw-string: " d-char-sequence(opt) ( r-char-sequence(opt) ) d-char-sequence(opt) " r-char-sequence: r-char r-char-sequence r-char r-char: any member of the source character set, except a right parenthesis ) followed by the initial d-char-sequence (which may be empty) followed by a double quote ". d-char-sequence: d-char d-char-sequence d-char d-char: any member of the basic source character set except: space, the left parenthesis (, the right parenthesis ), the backslash \, and the control characters representing horizontal tab, vertical tab, form feed, and newline.
这个的正则表达式是:
(?&prefix) R " (?<delimiter>[^ ()\\\t\x0B\r\n]*) \( (?s:.*?) \) \k<delimiter> "
Run Code Online (Sandbox Code Playgroud)
[^ ()\\\t\x0B\r\n]*是分隔符 ( d-char)中允许的字符集\k<delimiter> 指之前匹配的定界符完整的模式是:
(?(DEFINE)
(?<prefix> (?:u8?|U|L)? )
(?<escape> \\ (?:
['"?\\abfnrtv] # simple escape
| [0-7]{1,3} # octal escape
| x [0-9a-fA-F]{1,2} # hex escape
| u [0-9a-fA-F]{4} # universal character name
| U [0-9a-fA-F]{8} # universal character name
))
)
# singleline comment
// .* (*SKIP)(*FAIL)
# multiline comment
| /\* (?s: .*? ) \*/ (*SKIP)(*FAIL)
# character literal
| (?&prefix) ' (?> (?&escape) | [^'\\\r\n]+ )+ ' (*SKIP)(*FAIL)
# standard string
| (?&prefix) " (?> (?&escape) | [^"\\\r\n]+ )* "
# raw string
| (?&prefix) R " (?<delimiter>[^ ()\\\t\x0B\r\n]*) \( (?s:.*?) \) \k<delimiter> "
Run Code Online (Sandbox Code Playgroud)
请参阅此处的演示。
boost::regex这是一个简单的演示程序,使用boost::regex:
#include <string>
#include <iostream>
#include <boost/regex.hpp>
static void test()
{
boost::regex re(R"regex(
(?(DEFINE)
(?<prefix> (?:u8?|U|L) )
(?<escape> \\ (?:
['"?\\abfnrtv] # simple escape
| [0-7]{1,3} # octal escape
| x [0-9a-fA-F]{1,2} # hex escape
| u [0-9a-fA-F]{4} # universal character name
| U [0-9a-fA-F]{8} # universal character name
))
)
# singleline comment
// .* (*SKIP)(*FAIL)
# multiline comment
| /\* (?s: .*? ) \*/ (*SKIP)(*FAIL)
# character literal
| (?&prefix)? ' (?> (?&escape) | [^'\\\r\n]+ )+ ' (*SKIP)(*FAIL)
# standard string
| (?&prefix)? " (?> (?&escape) | [^"\\\r\n]+ )* "
# raw string
| (?&prefix)? R " (?<delimiter>[^ ()\\\t\x0B\r\n]*) \( (?s:.*?) \) \k<delimiter> "
)regex", boost::regex::perl | boost::regex::no_mod_s | boost::regex::mod_x | boost::regex::optimize);
std::string subject(R"subject(
std::cout << L"hello" << " world";
std::cout << "He said: \"bananas\"" << "...";
std::cout << "";
std::cout << "\x12\23\x34";
std::cout << u8R"hello(this"is\a\""""single\\(valid)"
raw string literal)hello";
"" // empty string
'"' // character literal
// this is "a string literal" in a comment
/* this is
"also inside"
//a comment */
// and this /*
"is not in a comment"
// */
"this is a /* string */ with nested // comments"
)subject");
std::cout << boost::regex_replace(subject, re, "String\\($&\\)", boost::format_all) << std::endl;
}
int main(int argc, char **argv)
{
try
{
test();
}
catch(std::exception ex)
{
std::cerr << ex.what() << std::endl;
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
(我禁用了语法高亮,因为它在这段代码上很疯狂)
出于某种原因,我不得不采取?量词出来的prefix(变化(?<prefix> (?:u8?|U|L)? )到(?<prefix> (?:u8?|U|L) )和(?&prefix)到(?&prefix)?),使模式的工作原理。我相信这是 boost::regex 中的一个错误,因为 PCRE 和 Perl 在原始模式下都可以正常工作。
请注意,虽然此模式在技术上使用递归,但它从不嵌套递归调用。通过将相关的可重用部分内联到主模式中,可以避免递归。
可以以降低性能为代价避免使用其他一些结构。我们可以安全地更换原子团(?>......)与正常对照组(?:......)如果我们为了避免不嵌套量词灾难性的回溯。
(*SKIP)(*FAIL)如果我们在替换函数中添加一行逻辑,我们也可以避免:所有要跳过的替代项都分组在一个捕获组中。如果捕获组匹配,则忽略匹配。如果不是,那么它是一个字符串文字。
所有这一切意味着我们可以在 JavaScript 中实现它,它拥有您能找到的最简单的正则表达式引擎之一,代价是打破 DRY 规则并使模式难以辨认。一旦转换,正则表达式就变成了这个怪物:
(\/\/.*|\/\*[\s\S]*?\*\/|(?:u8?|U|L)?'(?:\\(?:['"?\\abfnrtv]|[0-7]{1,3}|x[0-9a-fA-F]{1,2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8})|[^'\\\r\n])+')|(?:u8?|U|L)?"(?:\\(?:['"?\\abfnrtv]|[0-7]{1,3}|x[0-9a-fA-F]{1,2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8})|[^"\\\r\n])*"|(?:u8?|U|L)?R"([^ ()\\\t\x0B\r\n]*)\([\s\S]*?\)\2"
Run Code Online (Sandbox Code Playgroud)
这是您可以玩的交互式演示:
function run() {
var re = /(\/\/.*|\/\*[\s\S]*?\*\/|(?:u8?|U|L)?'(?:\\(?:['"?\\abfnrtv]|[0-7]{1,3}|x[0-9a-fA-F]{1,2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8})|[^'\\\r\n])+')|(?:u8?|U|L)?"(?:\\(?:['"?\\abfnrtv]|[0-7]{1,3}|x[0-9a-fA-F]{1,2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8})|[^"\\\r\n])*"|(?:u8?|U|L)?R"([^ ()\\\t\x0B\r\n]*)\([\s\S]*?\)\2"/g;
var input = document.getElementById("input").value;
var output = input.replace(re, function(m, ignore) {
return ignore ? m : "String(" + m + ")";
});
document.getElementById("output").innerText = output;
}
document.getElementById("input").addEventListener("input", run);
run();Run Code Online (Sandbox Code Playgroud)
<h2>Input:</h2>
<textarea id="input" style="width: 100%; height: 50px;">
std::cout << L"hello" << " world";
std::cout << "He said: \"bananas\"" << "...";
std::cout << "";
std::cout << "\x12\23\x34";
std::cout << u8R"hello(this"is\a\""""single\\(valid)"
raw string literal)hello";
"" // empty string
'"' // character literal
// this is "a string literal" in a comment
/* this is
"also inside"
//a comment */
// and this /*
"is not in a comment"
// */
"this is a /* string */ with nested // comments"
</textarea>
<h2>Output:</h2>
<pre id="output"></pre>Run Code Online (Sandbox Code Playgroud)
阅读 C++ 标准中的相关部分,它们称为lex.ccon和lex.string。
然后将您找到的每个规则转换为正则表达式(如果您确实想使用正则表达式;可能会发现它们无法完成这项工作)。
然后,用它们构建更复杂的正则表达式。请务必严格按照 C++ 标准中的规则命名正则表达式,以便稍后重新检查它们。
如果您不想使用正则表达式,而是想使用现有工具,可以使用以下工具: http: //clang.llvm.org/doxygen/Lexer_8cpp_source.html。看看这个LexStringLiteral函数。