云雀解析器预定义了一些常见的终端,包括字符串。它的定义如下:
_STRING_INNER: /.*?/
_STRING_ESC_INNER: _STRING_INNER /(?<!\\)(\\\\)*?/
ESCAPED_STRING : "\"" _STRING_ESC_INNER "\""
我了解_STRING_INNER
。我也了解ESCAPED_STRING
的组成。但是我不太了解的是_STRING_ESC_INNER
。
如果我正确阅读了正则表达式,它的意思是,每当我发现两个连续的文字反斜杠时,它们都不能以另一个文字反斜杠开头吗?
如何将这两个合并为一个正则表达式?
并且是否不要求语法只允许在字符串数据中使用转义的双引号?
初步:
.*?
非贪心匹配,表示.
(任何符号)的最短重复次数。这仅在后面有其他内容时才有意义。因此,输入.*?X
上的AAXAAX
仅匹配AAX
部分,而不是一直扩展到最后一个X
。
(?<!...)
是“负向后断言”(link):“如果字符串中的当前位置之前没有...的匹配项,则进行匹配。”因此.*(?<!X)Y
会匹配AY
,但不匹配XY
。
将其应用于您的示例:
ESCAPED_STRING
:规则说:“先匹配\"
,然后匹配_STRING_ESC_INNER
,然后再次匹配\"
”。
_STRING_INNER
:匹配任何符号的最短重复次数。如前所述,这仅在考虑其后的正则表达式时才有意义。
_STRING_ESC_INNER
:我们希望它与不包含结束转义引号的最短字符串匹配。也就是说,对于输入\"abc\"xyz\"
,我们要匹配\"abc\"
,而不是同时消耗xyz\"
部分。但是,我们必须确保\"
确实是转义的结束引号,因为\
本身不应转义。因此,对于输入\"abc\\"xyz\"
,我们不希望仅匹配\"abc\\"
,因为\\"
是一个\
,转义了\
,然后是"
。我们注意到,在结束\"
之前必须直接加上偶数\
(零是偶数)。因此,\"
正常,\\\"
正常,\\\\\"
正常,以此类推。但是,一旦\"
前面带有奇数\
,这意味着\"
并不是真正的转义结束符报价。
(\\\\)
匹配\\
。 (?<!\\)
说“之前的位置不应该有\
”。因此,组合的(?<!\\)(\\\\)
表示“匹配\\
,但前提是它不以\
开头。
随后的*?
然后进行尽可能小的重复,这仅在考虑到此之后的正则表达式(即\"
规则中的ESCAPED_STRING
)时才有意义。因此,(?<!\\)(\\\\)*?\"
的意思是“匹配最短的\\
数量,后跟\"
且不以\
开头。换句话说,(?<!\\)(\\\\)*?\"
仅匹配以偶数开头的\"
\
(包括大小为0的块)。
现在将其与前面的_STRING_INNER
组合在一起,然后_STRING_ESC_INNER
规则说:匹配first
\"
并加上偶数个\
,也就是说,第一个\"
\
本身未转义的位置。