为什么 /[\w-+]/ 是有效的正则表达式,但 /[\w-+]/u 无效?

Eli*_*ria 3 javascript regex unicode character-class

如果我/[\w-+]/在 Chrome 控制台中输入,它会接受它。我得到一个正则表达式对象,可以像往常一样用来测试字符串。但如果我输入/[\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 文档u启用了一些 Unicode 功能,但我没有看到任何关于它如何影响字符类范围的信息。

Ada*_*atz 8

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

字符范围的三个示例:一个简单示例、一个高级示例和一个错误:

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

ASCII 表


现在让我们检查一下您的正则表达式/[\w-+]/u。Regex101 有一个信息更丰富的错误:

您无法使用简写转义序列创建范围

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

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

语法错误:字符类转义不能在正则表达式的类范围中使用

这比您得到的错误信息稍微丰富一些,因为它实际上告诉您问题出在转义上(尽管不是为什么它是一个问题)。

根据ECMAScript 2015 的RegExBuiltinExec()逻辑

  1. 如果fullUnicodetrue,那么
  2. e是输入字符列表的索引,源自S ,由matcher匹配。令eUTF为S中与Input的元素e处的字符相对应的 最小索引。如果e大于或等于Input的长度,则eUTF是S中的代码单元数。
  3. eeUTF

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


解决方案是要么转义连字符减号,要么将其放在最后(或第一个):

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


rev*_*evo 7

有一个关于此的报告:V8实现:字符类范围内的unicode属性转义行为是否有意与其他类不同?


我查看了 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));
    }
Run Code Online (Sandbox Code Playgroud)

kRangeInvalid是一个保持 的常数Invalid character class

21.2.2.15.1 步骤 1。

如果A不完全包含一个字符或B不完全包含一个字符,则抛出SyntaxError异常。