【问题标题】:Can a Javascript RegExp match at an exact string offset without searching?Javascript RegExp 可以在不搜索的情况下以精确的字符串偏移量匹配吗?
【发布时间】:2011-10-21 17:42:39
【问题描述】:

我正在使用 Javascript 解析一个中等复杂的语法,我想使用正则表达式来匹配数字等标记。

给定一个包含语法的字符串、一个表示数字(比如)的正则表达式和字符串中的偏移量,我想知道正则表达式是否与该偏移量处的字符串完全匹配。

我可以设置 lastIndex,调用 RegExp.exec 并检查结果匹配的索引属性以查看匹配是否发生在预期的偏移量,但这非常低效,因为 exec 将搜索整个字符串,如果它没有找到在起始偏移处匹配。

Javascript 规范说“模式评估(“编译”)为内部过程值。RegExp.prototype.exec 然后可以将此过程应用于字符串和字符串中的偏移量,以确定模式是否从正是字符串中的偏移量。”

这正是我想要的,但似乎没有办法访问这个内部函数。谁知道有没有?

附:我目前正在通过将输入字符串拆分为令牌数组来避免此问题,但我不希望这样做。

【问题讨论】:

  • 我不确定我是否理解正确,但你不能改用str.substring(index) 并匹配其上的正则表达式以查看它是否与索引0 匹配?
  • @pimvdb - 请参阅下面我对 Rob 的回答

标签: javascript regex


【解决方案1】:

我已经彻底测试了可能有效的方法,请参阅 JSPerf:~20000 字符、~1000000 字符。我创建了一个函数来生成由字母数字字符组成的随机字符串。运行此函数一次后,将创建一个正则表达式模式,以匹配给定偏移量处长度为 10 的字符串。

测试用例(当if(..) 中的条件为真时,在偏移量index 处找到模式):

var string = "...about 20000 characters: A-Z a-z 0-9...";
var regexp = /abcdef1324/g;
var regexpSubstr = /^abcdefg1234/;
var index = 10000;

/*1*/ if ( regexpSubstr.exec(string.substr(index,10)) ) ;
/*2*/ regexp.lastIndex = index;
      var found =  regexp.exec(string);
      if (found && found.length + index == regexp.lastIndex ) ;

/*3*/ if ( regexpSubstr.test(string.substr(index,10)) ) ;
/*4*/ // Only when the RegExp matches a fixed number of characters
      regexp.lastIndex = index;
      if ( regexp.test(string) && regexp.lastIndex == index + 10 ) ;

Case 1Case 3 是等价的,因为它们正在检查子字符串是否与 /^abcdef1234/ 模式匹配(所选子字符串是否以 "abc..等等”?)。

案例 2案例 4 使用 .lastIndex 方法:
1. 将 RegExp 的 .lastIndex 属性设置为所需的偏移量
2. 检查是否找到模式。
3. 检查找到的模式是否位于偏移量index
这些方法需要一个正则表达式来启用全局标志。

在非常大的字符串中,方法 4 (lastIndex + test) 是 proved to be most efficient,当在偏移处发生匹配时。然而,方法 4 要求匹配的模式具有预定义的固定大小。

在给定位置发生匹配时,方法 3 (substr + test) 比 4 稍慢。但是,当在大字符串中找不到匹配项时,方法 3 是 significantly faster 而不是方法 4。方法 1 和方法 3 在找不到匹配项时似乎是 equally fast

正则表达式方法
.exec 似乎并不比.test 更有效。 match 方法不适合这种情况,因为它会尝试查找所有匹配项,而不管.lastIndex 属性如何。另一个可能的 RegExp 函数是 .search 函数,与之前显示的方法相比,它对于大字符串的速度要慢得多。

【讨论】:

  • 出于偏好,我想一次性扫描整个输入,而不创建大量的临时子字符串。这当然是可能的,但我不确定是否可以在 Javascript 中有效地做到这一点并使用正则表达式。虽然,实际上,正则表达式也会创建很多临时子字符串,所以也许这是最好的解决方案。
  • @arx 如果您知道您的起始偏移量是123354...,那么从位置 123354...获取子字符串比在 RegExp 中包含偏移量 0 到 123354... 更有效搜索查询。
  • 这不是问题。我可以使用正则表达式的 lastIndex 属性在 123354 开始搜索。如果我没有找到匹配项,我想避免搜索字符串的其余部分,您也可以解决这个问题,但代价是创建一个新字符串。然而,正如我上面所说,我开始认为这是一个合理的价格。
  • @arx 用经过全面测试的案例更新了答案。
  • 感谢您的透彻分析。但是,我担心比赛没有发生的情况。例如,如果您使用推荐的方法 4 并且找不到匹配项,它将扫描 1000000 个索引字符,这肯定会比方法 1 或 3 慢。您是否仅测试在给定偏移量?
【解决方案2】:

如果可以设置搜索起始位置,或许可以利用
断言中的结束锚点在真正的不匹配时终止。

它要求你接受它唯一一次不匹配的事实(表面上)
是当该起始位置落在字符串的末尾时。

然后进行后期处理以检查捕获缓冲区的长度。这取决于正则表达式,
但如果捕获缓冲区的长度为零,则可能会失败。

示例:
(\d{6} | (?!$))( (?:any subexpression) | (?!$) )

这将始终匹配(除非起始位置在字符串的末尾,或者
字符串为空)。

结果
- 不匹配:字符串为空,或起始位置在字符串的末尾。
- 匹配:%99.999 次。如果捕获缓冲区的长度为 0(即:''),则
交替的左侧失败,或者它没有捕获任何东西通过,依赖于正则表达式。

开始位置已得到处理,但是,正则表达式中的静态长度匹配有时很难(并非不可能)用量词来控制。它可能更多
在开放式量词的情况下,对子字符串使用正则表达式是合理的。

【讨论】:

    【解决方案3】:

    也许是new RegExp("^.{" + offset + "}whatever"),即任何字符正好偏移时间,然后是什么?需要基准测试,但希望 RegExp 优化“任何字符完全偏移时间”部分?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-07
      • 1970-01-01
      • 1970-01-01
      • 2015-05-15
      • 1970-01-01
      • 2011-10-18
      相关资源
      最近更新 更多