正则表达式模式。
正则表达式(简称regex或regexp)由一串字符组成,这些字符指定了用于文本输入的匹配检查算法。对一个输入文本应用正则表达式会导致正则表达式匹配、接受该文本,或拒绝该文本。当正则表达式匹配文本时,它还进一步提供了一些关于它的匹配方式的详细信息。
Dart 的正则表达式与 JavaScript 的正则表达式的语法和语义相同。要了解更多关于 JavaScript 正则表达式的内容,请参阅 ecma-international.org/ecma-262/9.0/#sec-regexp-regular-expression-objects。
Dart 提供了基本的正则表达式匹配算法,作为 matchAsPrefix,该算法检查正则表达式是否匹配输入的部分从特定位置开始。如果正则表达式匹配,Dart 则作为 RegExpMatch 返回匹配的详情。
您可以从基本匹配检查构建 RegExp 的其他所有方法。
正则表达式最常见的用途是在输入中 搜索 匹配。 firstMatch 方法提供此功能。该方法在字符串中搜索正则表达式首次匹配的位置。再次,如果找到匹配,Dart 则以 RegExpMatch 的形式返回其详情。
以下示例在字符串中查找正则表达式的第一个匹配项。
RegExp exp = RegExp(r'(\w+)');
String str = 'Parse my string';
RegExpMatch? match = exp.firstMatch(str);
print(match![0]); // "Parse"
使用 allMatches 在字符串中查找所有正则表达式的匹配项。
以下示例在字符串中查找所有正则表达式的匹配项。
RegExp exp = RegExp(r'(\w+)');
String str = 'Parse my string';
Iterable<RegExpMatch> matches = exp.allMatches(str);
for (final m in matches) {
print(m[0]);
}
示例的输出是
Parse
my
string
前面的示例使用了一个 原始字符串,这是一种特定类型的字符串,它在前缀字符串字面量前加上了 r
。使用原始字符串可以将字符串中的每个字符(包括 \
和 $
),都作为字面字符处理。然后,每个字符都会传递给 RegExp 解析器。您应将原始字符串作为 RegExp 构造函数的参数。
性能注意:正则表达式并不能神奇地解决问题。任何人都可以编写一个在应用于某些字符串输入时效率低下的正则表达式。通常,这种正则表达式对于小型或常见的输入足够高效,但对于大型和不常见的输入则性能异常。这种不一致的行为使得在测试中难以检测性能问题。
正则表达式可能不会比使用 String
操作检查字符串更快地找到文本。正则表达式的优势在于能够在极少数字符中指定相对复杂的模式。这些正则表达式在大多数常见情况下提供了合理的效率。这种简洁性是以可读性为代价的。由于它们的语法复杂性,正则表达式不能被认为是自文档化的。
Dart 正则表达式实现了 ECMAScript RegExp 规范。该规范提供了既常见又为人熟知的正则表达式行为。当将 Dart 编译为 Web 时,编译后的代码可以使用浏览器的正则表达式实现。
本规范使用回溯来定义ECMAScript正则表达式的行为。当正则表达式可以选择多种匹配方式时,它将按照模式中给出的顺序尝试每种方式。例如:RegExp(r"(foo|bar)baz")
想要检查foo
或bar
,所以它首先检查foo
。如果沿着这条路径继续不匹配输入,正则表达式实现就会回溯。实施将重置到在检查foo
之前的原始状态,忘记所有在此之后完成的工作,然后尝试下一个选择;在这个例子中是bar
。
本规范定义了这些选择和必须尝试的顺序。如果一个正则表达式可以用多种方式匹配输入,那么选择的顺序将决定哪个匹配是正则表达式返回的。常用正则表达式在匹配选择上是按顺序排列的,以确保特定的结果。ECMAScript正则表达式规范限制了Dart实现正则表达式的方式。它必须是一个回溯实现,按照特定的顺序检查选择。Dart不能选择不同的正则表达式实现,因为在其他情况下,正则表达式的匹配行为将不同。
回溯方法有效,但代价很大。对于某些正则表达式和某些输入,找到正确的匹配可能需要进行很多尝试。拒绝一个正则表达式几乎匹配的输入可能需要更多的尝试。
一个众所周知的危险的正则表达式模式来自像*
这样的量词嵌套。
var re = RegExp(r"^(a*|b)*c");
print(re.hasMatch("aaaaaaaaaaaaaaaaaaaaaaaaaaaaa"));
正则表达式模式不匹配只有a
的输入,因为输入不包含所需的c
。存在大量不同的方式,使得(a*|b)*
能够匹配所有的a
。回溯正则表达式实现在决定这些没有一个能导致完全匹配之前,会尝试所有这些方式。输入中添加每个额外的a
会将正则表达式返回false
所需的时间翻倍。(当回溯具有这种指数潜力时,被称为“灾难性回溯”)。
顺序量词提供了另一种危险的模式,但它们提供的“只是”多项式复杂度。
// Like `\w*-\d`, but check for `b` and `c` in that order.
var re = RegExp(r"^\w*(b)?\w*(c)?\w*-\d");
print(re.hasMatch("a" * 512));
再次,输入不匹配,但RegExp
必须尝试n3种方法来匹配这n个a
,在决定失败之前。输入长度加倍将使返回false
的时间增加八倍。这个指数随着顺序量词数量的增加而增加。
这两种模式在简化到这样的简单正则表达式时看起来很平凡。然而,这些“平凡”模式通常作为更复杂正则表达式的一部分出现,其中发现问题变得更困难。
一般来说,如果一个正则表达式有超线性复杂性的潜力,你可以构建一个输入,搜索它将花费不正常的时间。如果你将这些易受攻击的正则表达式模式应用于用户提供的输入,则可以使用这些模式进行拒绝服务攻击。
对于这个问题没有保证的解决方案。请小心不要使用在可能匹配没有任何保证的输入的程序中具有超线性行为的正则表达式。
避免具有超线性执行时间的正则表达式的经验法则包括
- 无论何时正则表达式都有选择,请确保选择可以根据下一个字符(或非常有限的向前查看)进行。这限制了需要在两种选择上进行大量计算的必要性。
- 当使用量词时,请确保相同的字符串不能匹配量词正则表达式的一次或多次迭代。(对于
(a*|b)*
,字符串"aa"
可以匹配(a*|b){1}
和(a*|b){2}
。) - 大多数Dart正则表达式的用法是搜索匹配,例如使用firstMatch。如果您不使用
^
将模式锚定到行的开头或输入,此搜索将与正则表达式从隐式的[^]*
开始进行交互。然后,以.*
开始实际的正则表达式会导致搜索的二次行为。在适当的地方使用锚点、matchAsPrefix,或者避免使用量词模式开始正则表达式。 - 仅供专家: Dart和ECMAScript都没有通用的“原子分组”。其他正则表达式方言使用此限制回溯。如果一个原子捕获组成功匹配一次,正则表达式就不能回溯到相同的匹配中。因为先行断言也充当原子组,所以可以使用先行断言实现类似的结果:
var re = RegExp(r"^(?=((a*|b)*))\1d");
前面的例子执行与(a*|b)*
相同的低效匹配。正则表达式尽可能多地匹配后,它完成正的先行断言。然后它使用回溯引用跳过先行断言所匹配的部分。在那之后,它无法回溯并尝试其他a
的组合。
尽量减少正则表达式匹配相同字符串的方式。这减少了当正则表达式找不到匹配时执行的可能的回溯次数。互联网上存在多个关于提高正则表达式性能的指南。将其作为灵感的来源,也可以。
- 实现类型
构造函数
属性
- hashCode → int
- 此对象的哈希码。no setterinherited
- isCaseSensitive → bool
- 此正则表达式是否为大小写敏感。no setter
- isDotAll → bool
- 此正则表达式中的点(
.
)是否匹配行终止符。no setter - isMultiLine → bool
- 此正则表达式是否匹配多行。no setter
- isUnicode → bool
- 此正则表达式是否使用Unicode模式。no setter
- pattern → String
- 此
RegExp
的正则表达式模式源。no setter - runtimeType → Type
- 对象的运行时类型的表示。no setterinherited
方法
-
allMatches(
String input, [int start = 0]) → Iterable< RegExpMatch> - 对字符串进行模式的反复匹配。覆盖
-
firstMatch(
String input) → RegExpMatch? - 在字符串中找到正则表达式的第一个匹配项。
-
hasMatch(
String input) → bool - 检查该正则表达式在中是否有匹配。
-
matchAsPrefix(
String string, [int start = 0]) → Match? - 将此模式与字符串的开始进行匹配。继承
-
noSuchMethod(
Invocation invocation) → dynamic - 当访问不存在的方法或属性时调用。继承
-
stringMatch(
String input) → String? - 在中找到此正则表达式的第一个匹配项的字符串。
-
toString(
) → String - 此对象的字符串表示。继承
操作符
-
operator ==(
Object other) → bool - 等号操作符。继承