39. 正则表达式(RegExp)

优质
小牛编辑
112浏览
2023-12-01

原文: http://exploringjs.com/impatient-js/ch_regular-expressions.html

功能的可用性

除非另有说明,否则 ES5 及更高版本支持所有正则表达式功能。

39.1. 创建正则表达式

39.1.1. 字面义式 vs. 构造函数式

创建正则表达式的两种主要方法是:

  • 字面义式:/abc/ui,被静态编译(在加载时)。
  • 构造函数式:new RegExp('abc', 'ui'),被动态编译(在运行时)
    • 第二个参数是可选的。

两个正则表达式都有相同的两个部分:

  • 表达式体 abc - 实际正则表达式。
  • 标志 ui。标志配置了表达式模式如何被解释:u切换到 _Unicode 模式 _ 。 i启用大小写不敏感匹配。

39.1.2. 克隆和非破坏性地修改正则表达式

构造函数RegExp()有两种变体:

  • new RegExp(pattern : string, flags = '')

    通过指定的pattern创建新的正则表达式。如果缺少flags,则使用空字符串''

  • new RegExp(regExp : RegExp, flags = regExp.flags)[ES6]

    regExp被克隆。如果提供了flags,则它确定副本的标志。

第二个变体可用于克隆正则表达式,在修改它们时也是可选的。标志是不可变的,这是改变它们的唯一方法。例如:

function copyAndAddFlags(regExp, flags='') {
  // The constructor doesn’t allow duplicate flags,
  // make sure there aren’t any:
  const newFlags = [...new Set(regExp.flags + flags)].join('');
  return new RegExp(regExp, newFlags);
}
assert.equal(/abc/i.flags, 'i');
assert.equal(copyAndAddFlags(/abc/i, 'g').flags, 'gi');

39.2. 语法

39.2.1. 语法字符

在正则表达式的顶层,以下 语法字符 是特殊的。它们通过前缀反斜杠(\)进行转义。

\ ^ $ . * + ? ( ) [ ] { } |

在正则表达式字面义中,您还必须转义斜杠(用new RegExp()就没有必要了):

> /\//.test('/')
true
> new RegExp('/').test('/')
true

39.2.2. 基本原子

原子 是正则表达式的基本构建块。

  • 模式字符:除语法字符(^$等)外的所有字符。模式字符匹配自己。示例:A b %
  • .匹配任何字符。您可以使用标志/sdotall)来控制点.是否与行终止符匹配(下面详述)。
  • 字符转义(每个转义匹配一个固定字符):
    • 控制转义(用于一些控制字符):
      • \f:换页(FF)
      • \n:换行(LF)
      • \r:回车(CR)
      • \t:角色列表
      • \v:行列表
    • 任意控制字​​符:\cA(Ctrl-A),\cB(Ctrl-B)等。
    • Unicode 代码单位:\u00E4
    • Unicode 代码点(需要标志/u):\u{1F44D}
  • 字符类转义(每个转义匹配一组字符中的一个):
    • \d:数字(与[0-9]相同)
      • \D:非数字
    • \w:“单词”字符(与[A-Za-z0-9_]相同)
      • \W:非单词字符
    • \s:空白(空格,制表符,行终止符等)
      • \S:非空白
    • Unicode 属性转义(ES2018):\p{White_Space}\P{White_Space}等.
      • 需要标志/u
      • 在下一小节中描述。
39.2.2.1. Unicode 属性转义

Unicode 属性转义看起来像这样:

  1. \p{prop=value}:匹配属性prop具有值value的所有字符。
  2. \P{prop=value}:匹配所有没有属性prop的字符,其值为value
  3. \p{bin_prop}:匹配二进制属性bin_prop为 True 的所有字符。
  4. \P{bin_prop}:匹配二进制属性bin_prop为 False 的所有字符。

评论:

  • 如果设置了标志/u,则只能使用 Unicode 属性转义。没有/u\pp相同。

  • 如果属性是General_Category,则表格(3)和(4)可以用作缩写。例如,\p{Lowercase_Letter}\p{General_Category=Lowercase_Letter}的缩写

例子:

  • 检查空格:

    > /^\p{White_Space}+$/u.test('\t \n\r')
    true
    
  • 检查希腊字母:

    > /^\p{Script=Greek}+$/u.test('μετά')
    true
    
  • 删除任何字母:

    > '1π2ü3é4'.replace(/\p{Letter}/ug, '')
    '1234'
    
  • 删除小写字母:

    > 'AbCdEf'.replace(/\p{Lowercase_Letter}/ug, '')
    'ACE'
    

进一步阅读:

39.2.3。角色类

  • 匹配一组字符中的一个:[abc]

    • 匹配不在集合中的任何字符:[^abc]
  • 在方括号内,只有以下字符是特殊的,必须进行转义:

    ^ \ - ]
    

    ^只有先到时才需要进行转义。 -如果是第一个或最后一个,则无需转义

  • 字符转义(\n\u{1F44D})和字符类转义(\d\p{White_Space})照常工作。

    • 例外:在方括号内,\b匹配退格。在其他地方,它匹配单词边界。
  • 字符范围通过短划线指定:[a-z][^a-z]

39.2.4. 组

  • 位置捕获组:(#+)
    • 反向引用:\1\2
  • 命名捕获组(ES2018):(?<hashes>#+)
    • 反向引用:\k<hashes>
  • 非捕获组:(?:#+)

39.2.5. 量词

默认情况下,以下所有量词都是贪心的:

  • ?:匹配从不或一次
  • *:匹配零次或多次
  • +:匹配一次或多次
  • {n}:匹配n
  • {n,}:匹配n次或更多次
  • {n,m}:至少匹配n次,最多m次。

为了使他们不情愿,在他们后面加上问号(?):

> /".*"/.exec('"abc"def"')[0]  // greedy
'"abc"def"'
> /".*?"/.exec('"abc"def"')[0] // reluctant
'"abc"'

39.2.6. 断言

  • ^仅在输入的开头匹配

  • $仅在输入结束时匹配

  • \b仅匹配单词边界

    • \B仅在不在单词边界时匹配
  • 向前看:

    • 如果pattern匹配下一个(积极前瞻),则(?=«pattern»)匹配。示例(“X后跟小写字母的序列” - 请注意X本身不是匹配的子字符串的一部分):
    > 'abcX def'.match(/[a-z]+(?=X)/g)
    [ 'abc' ]
    
    • 如果pattern与接下来的内容不匹配,则(?!«pattern»)匹配(消极前瞻)。示例(“小写字母的序列,后面没有X”)
    > 'abcX def'.match(/[a-z]+(?!X)/g)
    [ 'ab', 'def' ]
    
  • 向后看 (ES2018):

    • 如果pattern与之前的相符,则(?<=«pattern»)匹配(积极后观)
    > 'Xabc def'.match(/(?<=X)[a-z]+/g)
    [ 'abc' ]
    
    • 如果pattern与之前的不匹配,则(?<!«pattern»)匹配(消极后观)
    > 'Xabc def'.match(/(?<!X)[a-z]+/g)
    [ 'bc', 'def' ]
    

39.2.7. 分离(|

警告:此运算符的优先级较低。必要时使用组:

  • ^aa|zz$匹配以aa开头和/或以zz结束的所有字符串。请注意,|的优先级低于^$
  • ^(aa|zz)$匹配两个字符串'aa''zz'
  • ^a(a|z)z$匹配两个字符串'aaz''azz'

39.3. 标志

表格 20: 这些是JavaScript支持的正则表达式标志。

字面义标志属性名称ES描述
gglobalES3匹配多次
iignoreCaseES3不区分大小写
mmultilineES3每行^$匹配
sdotallES2018.匹配行终止符
uunicodeES6Unicode 模式(推荐)
ystickyES6匹配之间没有字符

JavaScript 中提供了以下正则表达式标志(表格20 提供了紧凑的概述):

  • /g.global):从根本上改变方法RegExp.prototype.test()RegExp.prototype.exec()String.prototype.match()的工作方式。将与这些方法一起详细解释。简而言之:如果没有/g,方法只考虑输入字符串中正则表达式的第一个匹配项。使用/g,他们会考虑所有匹配。

  • /i.ignoreCase):打开不区分大小写的匹配:

> /a/.test('A')
false
> /a/i.test('A')
true
  • /m.multiline):如果该标志打开,^匹配每一行的开头,$匹配每一行的结尾。如果它关闭,^匹配整个输入字符串的开头,$匹配整个输入字符串的结尾。
> 'a1\na2\na3'.match(/^a./gm)
[ 'a1', 'a2', 'a3' ]
> 'a1\na2\na3'.match(/^a./g)
[ 'a1' ]
  • /u.unicode):该标志用于打开正则表达式的 Unicode 模式。该模式将在下一小节中解释。

  • /y.sticky):该标志仅与/g一起使用。当两者都打开时,第一个之后的任何匹配必须直接跟随前一个匹配(它们之间没有任何字符)。

> 'a1a2 a3'.match(/a./gy)
[ 'a1', 'a2' ]
> 'a1a2 a3'.match(/a./g)
[ 'a1', 'a2', 'a3' ]
  • /s.dotall):默认情况下,点与行终止符不匹配。有了这个标志,它确实:
> /./.test('\n')
false
> /./s.test('\n')
true

旧版 ECMAScript 版本的替代方案:

> /[^]/.test('\n')
true

39.3.1. 标志:通过/u的 Unicode 模式

标志/u为正则表达式打开特殊的 Unicode 模式。该模式支持多种功能:

  • 在模式中,您可以使用 Unicode 代码点转义(例如\u{1F42A})来指定字符。诸如\u03B1之类的代码单元转义只有四个十六进制数字的范围(等于基本的多语言平面)。

  • 在模式中,您可以使用 Unicode 属性转义(ES2018),例如\p{White_Space}

  • 现在禁止许多转义(这使得之前的功能成为可能):

> /\a/
/\a/
> /\a/u
SyntaxError: Invalid regular expression: /\a/: Invalid escape

> /\-/
/\-/
> /\-/u
SyntaxError: Invalid regular expression: /\-/: Invalid escape

> /\:/
/\:/
> /\:/u
SyntaxError: Invalid regular expression: /\:/: Invalid escape
  • 匹配的原子单位(“字符”)是代码点,而不是代码单元。

以下小节将更详细地解释最后一项。它们使用以下 Unicode 字符来解释原子单位何时是代码点以及何时是代码单元:

const codePoint = '