在regex101中徘徊几分钟之后,我意识到]
不需要逃脱,如果它立即跟随[
。
在regex101中,[]-a-z]
模式被描述为
/[]-a-z]/ []-a-z] match a single character present in the list below ]-a a single character in the range between ] and a (case sensitive) -z a single character in the list -z literally (case sensitive)
但我一直认为,如果-
必须在字面上匹配而不被逃脱,它应该either go at the beginning, or at end。
那么为什么我的模式不被识别为错误?为什么-z
字面上匹配-z
列表中的单个字符?
让我们分解一下:
[]-a-z]
^^ ^
|| +---- 3
|+------ 2
+------- 1
1
是文字]
,因为它出现在模式的开头,而[]
是PCRE中的无效字符类。
因此,2
连字符是该类中的第二个字符,并引入了]
和a
之间的范围。
下一个连字符3
按字面处理,因为前一个标记a
是前一个范围的结尾。此时不能引入另一个范围。在PCRE中,如果-
位于无法引入范围或者它被逃逸的地方,则会对其进行字面处理。我们通常将字面连字符放在范围的开头或结尾以使其显而易见,但这不是必需的。
然后,z
是一个简单的文字。
PCRE遵循Perl语法。这是documented像这样:
关于]
:
]
通常是POSIX字符类的末尾(参见下面的POSIX字符类),或者它标示括号字符类的结尾。如果你想在字符集中包含]
,你通常必须逃避它。 但是,如果]
是括号中的字符类的第一个(或第二个,如果第一个字符是插入符号)字符,它不表示类的结尾(因为你不能有一个空类)并且被认为是可以在不转义的情况下匹配的字符集。
关于连字符:
如果字符类中的连字符在语法上不能成为范围的一部分,例如因为它是字符类的第一个或最后一个字符,或者它紧跟一个范围,则连字符不是特殊的,因此被认为是一个字面上要匹配的字符。如果要匹配字符集中的连字符并且它在类中的位置使得它可以被视为范围的一部分,则必须使用反斜杠来转义该连字符。
请注意,这是指Perl语法。其他口味可能有不同的行为。例如,[]
是JavaScript中的有效(空)字符类,无法匹配任何内容。
问题在于,根据选项,PCRE也可以用JS方式解释这个(有几个JS兼容性标志)。来自PCRE2 docs:
开始方括号引入了一个字符类,以一个右方括号结束。默认情况下,关闭方括号本身并不特殊。如果需要结束方括号作为类的成员,它应该是类中的第一个数据字符(在初始抑扬符之后,如果存在)或使用反斜杠转义。这意味着,默认情况下,无法定义空类。但是,如果设置了
PCRE2_ALLOW_EMPTY_CLASS
选项,则开头的结束方括号将结束(空)类。
关于连字符的记录的PCRE行为不出所料地匹配Perl行为:
减号(连字符)字符可用于指定字符类中的字符范围。例如,
[d-m]
匹配d和m之间的任何字母,包括端点。如果类中需要减号,则必须使用反斜杠进行转义,或者出现在不能将其解释为指示范围的位置,通常作为类中的第一个或最后一个字符,或紧接在范围之后。例如,[b-d-z]
将b
范围内的字母与d
(连字符或z
)匹配。
在无法形成范围的字符类中的其他位置处的连字符可能被解释为文字或错误。正则表达式的味道与此非常不一致。
所以,这里-
不能形成一个范围,因为前一个标记是一个范围而不是一个字符因此它被解释为文字-
正则表达式没有失败,因为-
意味着这里的范围,从]
到a
。 ]
不必在字符类中的起始位置进行转义,因此在此处将其视为文字。字符类有效,因为]
有一个93
ASCII码,而a
在ASCII表中有一个97
代码。
编辑:
关于正则表达式,有一件事是普遍的:它们从左到右进行分析。因此,使用第一连字符周围的第一个字符形成范围。第二个连字符紧跟在范围结束字符之后,因为它被“占用”,所以它不能用作起始范围字符。因此,正则表达式引擎不能只解析第二个连字符作为文字
减号(连字符)字符可用于指定字符类中的一系列字符。例如,[d-m]匹配d和m之间的任何字母,包括端点。如果类中需要减号,则必须使用反斜杠进行转义,或者出现在不能将其解释为指示范围的位置,通常作为类中的第一个或最后一个字符,或紧接在范围之后。例如,[b-d-z]匹配范围b到d,连字符或z中的字母。