【问题标题】:Why is /[\w-+]/ a valid regex but /[\w-+]/u invalid?为什么 /[\w-+]/ 是一个有效的正则表达式,但 /[\w-+]/u 无效?
【发布时间】:2019-06-09 20:08:33
【问题描述】:

如果我在 Chrome 控制台中输入 /[\w-+]/,它会接受它。我得到了一个正则表达式对象,我可以像往常一样使用它来测试字符串。但是如果我输入/[\w-+]/u,它会显示VM112:1 Uncaught SyntaxError: Invalid regular expression: /[\w-+]/: Invalid character class

在 Firefox 中,/[\w-+]/ 可以正常工作,但如果我在控制台中输入 /[\w-+]/u,它只会转到下一行,就好像我输入了一个不完整的语句一样。如果我试图通过运行eval('/[\w-+]/u') 强制它创建正则表达式,它会告诉我SyntaxError: invalid range in character class

为什么u 标志会使正则表达式无效? MDN RegExp documentationu 启用了一些 Unicode 功能,但我没有看到它如何影响字符类中的范围。

【问题讨论】:

  • u 修饰符使正则表达式引擎以更严格的方式解析正则表达式。所有不必转义的字符都不能转义,而那些必须转义的字符。必须避免所有歧义。
  • 好的,所以ECMA-262,第 570 页,注 3,说“- 字符可以按字面处理,也可以表示一个范围。如果它是第一个或ClassRanges 的最后一个字符,范围规范的开始或结束限制,或紧跟范围规范"。
  • 并且:ClassRanges 可以扩展为单个 ClassAtom 和/或由破折号分隔的两个 ClassAtom 的范围。在后一种情况下,ClassRanges 包括第一个 ClassAtom 和第二个 ClassAtom 之间的所有字符,包括在内; 如果 ClassAtom 不代表单个字符(例如,如果一个字符是 \w)或如果第一个 ClassAtom 的字符值大于第二个 ClassAtom 的字符值,则会发生错误。 (link)
  • @WiktorStribiżew,您的引用似乎解释了为什么正则表达式会导致错误。但我没有看到任何关于为什么错误只发生在 u 标志上的任何信息。
  • 我希望 Mathias Bynens 能来分享他的想法。

标签: javascript regex unicode character-class


【解决方案1】:

RegExp character set 中,hyphen-minus 字符(您的标准键盘破折号)表示它分隔的两个字符之间的字符代码范围。例外情况是它被转义 (\-) 或它没有分隔两个字符,因为它要么是类的最后一个字符,要么是第一个字符(在反转类的可选插入符号之后)。

字符范围的三个例子:一个简单的例子,一个高级的例子,一个bug:

  • [a-z] 非常简单,因为它按照我们期望的方式工作,尽管这实际上是因为字符代码恰好是连续的。另一种写法是[\x61-\x7a]
  • [!-~] 一点也不简单,至少在您查看字符映射并了解 ! 是第一个可打印的 ASCII 字符而 ~ 是最后一个(“较低的 ASCII”)之前,这是一种方法说“所有可打印的低 ASCII 字符”,它相当于 [\x21-\x7e]
  • [A-z] 里面有一个开关盒。你可能不喜欢这个范围接受六个非字母字符(即[\x41-\x7a]


现在让我们检查您的正则表达式 /[\w-+]/u Regex101 有一个信息量更大的错误:“您无法使用速记转义序列创建范围”

由于\w 本身不是一个字符(而是一个字符的集合),因此必须按字面理解相邻的破折号,否则会出错。当您使用/u flag 调用它以触发fullUnicode 时,您会进入更严格的模式,因此会出现错误。

我在 Firefox 64.0 中从 "foo".match(/[\w-+]/u) 得到的错误是:

SyntaxError: character class escape cannot be used in class range in regular expression

这比您得到的错误信息略多一些,因为它实际上告诉您问题出在转义上(尽管不是问题的原因)。

根据ECMAScript 2015's RegExBuiltinExec() logic

  1. 如果 fullUnicodetrue,则
    1. eInput 字符列表的索引,源自 S,由 matcher 匹配。让 eUTF 成为 S 中对应于 Input 的元素 e 处的字符的最小索引。如果e大于等于Input的长度,那么eUTF就是S中的码元个数。时间>
    2. eeUTF

这似乎是在明确构建自己的范围解析逻辑。


解决方案要么转义你的连字符减号,要么把它放在最后(或第一个):

/[\w\-+]/u/[\w+-]/u/[-\w+]/u。我个人总是把它放在最后。

【讨论】:

    【解决方案2】:

    对此有报告:V8 implementation: does unicode property escapes behavior in character classes range differ from other classes intentionally?


    我查看了 V8 源代码 (regexp-parser.cc) 发现了这个:

    if (is_class_1 || is_class_2) {
        // Either end is an escaped character class. Treat the '-' verbatim.
        if (unicode()) {
           // ES2015 21.2.2.15.1 step 1.
           return ReportError(CStrVector(kRangeInvalid));
        }
    

    kRangeInvalid 是一个包含Invalid character class 的常量。

    21.2.2.15.1 step 1.

    如果A 不包含一个字符或B 不包含 正好一个字符,抛出一个SyntaxError 异常。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-12-31
      • 1970-01-01
      • 2011-03-01
      • 2018-10-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多