在JavaScript正则表达式匹配中移动索引

时间:2012-12-29 12:59:41

标签: javascript regex

我有这个正则表达式从文本中提取双字

/[A-Za-z]+\s[A-Za-z]+/g

此示例文本

Mary had a little lamb

我的输出是

[0] - Mary had; [1] - a little;

而我的预期输出是:

[0] - Mary had; [1] - had a; [2] - a little; [3] - little lamb

如何实现此输出?据我了解,搜索索引移动到第一场比赛结束。我怎样才能将它移回一个单词?

6 个答案:

答案 0 :(得分:5)

滥用String.replace函数

我使用replace函数使用了一个小技巧。由于replace函数循环匹配并允许我们指定函数,因此可能性是无限的。结果将在output

var output = [];
var str = "Mary had a little lamb";
str.replace(/[A-Za-z]+(?=(\s[A-Za-z]+))/g, function ($0, $1) {
    output.push($0 + $1);
    return $0; // Actually we don't care. You don't even need to return
});

由于输出包含输入字符串中的重叠部分,因此当我们使用 预测 匹配当前单词时,不必使用下一个单词 1

正则表达式/[A-Za-z]+(?=(\s[A-Za-z]+))/g与我上面所说的完全一样:它只会在[A-Za-z]+部分(正则表达式的开头)一次只消耗一个字,并且预测下一个单词(?=(\s[A-Za-z]+)) 2 ,还捕获匹配的文本。

传递给replace函数的函数将接收匹配的字符串作为第一个参数,并在后续参数中接收捕获的文本。 (还有更多 - 检查documentation - 我在这里不需要它们)。由于前瞻是零宽度(输入不被消耗),所以整个匹配也方便地成为第一个单词。前瞻中的捕获文本将进入第二个参数。

使用RegExp.exec

的正确解决方案

请注意,String.replace函数会产生替换开销,因为根本不使用替换结果。如果这是不可接受的,您可以在循环中使用RegExp.exec函数重写上面的代码:

var output = [];
var str = "Mary had a little lamb";
var re = /[A-Za-z]+(?=(\s[A-Za-z]+))/g;
var arr;

while ((arr = re.exec(str)) != null) {
    output.push(arr[0] + arr[1]);
}

脚注

  1. 在支持可变宽度负面后视的其他正则表达式中,可以检索前一个单词,但JavaScript正则表达式不支持负面后卫!。

  2. (?=pattern)是预见的语法。

  3. 附录

    此处不能使用

    String.match,因为它在使用g标志时会忽略捕获组。正则表达式中必须使用捕获组,因为我们需要环顾四周以避免消耗输入并匹配重叠文本。

答案 1 :(得分:4)

可以在没有正则表达式的情况下完成

"Mary had a little lamb".split(" ")
      .map(function(item, idx, arr) { 
          if(idx < arr.length - 1){
              return item + " " + arr[idx + 1];
          }
       }).filter(function(item) {return item;})

答案 2 :(得分:2)

这是一个非正则表达式解决方案(这不是一个常见的问题)。

function pairs(str) {
  var parts = str.split(" "), out = [];
  for (var i=0; i < parts.length - 1; i++) 
    out.push([parts[i], parts[i+1]].join(' '));
  return out;
}

传递你的字符串然后你得到一个数组。

demo


旁注:如果您担心输入中的非单词(为正则表达式做出判断!),您可以在parts[i]内的parts[i+1]for上运行测试环。如果测试失败:请勿将它们推到out

答案 3 :(得分:1)

你可能喜欢的方式就是这个:

var s = "Mary had a little lamb";

// Break on each word and loop
s.match(/\w+/g).map(function(w) {

    // Get the word, a space and another word
    return s.match(new RegExp(w + '\\s\\w+'));

// At this point, there is one "null" value (the last word), so filter it out
}).filter(Boolean)

// There, we have an array of matches -- we want the matched value, i.e. the first element
.map(Array.prototype.shift.call.bind(Array.prototype.shift));

如果您在控制台中运行此功能,则会看到["Mary had", "had a", "a little", "little lamb"]

通过这种方式,您可以保留原始正则表达式并可以执行其他所需的内容。虽然有一些代码可以使它真正起作用。

顺便说一下,这段代码不是跨浏览器的。 IE8及以下版本不支持以下功能:

  • Array.prototype.filter
  • Array.prototype.map
  • Function.prototype.bind

但它们很容易变质。或者for可以轻松实现相同的功能。

答案 4 :(得分:0)

我们走了:

你仍然不知道正则表达式内部指针是如何工作的,所以我将用一个小例子向你解释:

Mary had a little lamb使用此正则表达式/[A-Za-z]+\s[A-Za-z]+/g

此处,正则表达式的第一部分:[A-Za-z]+将与Mary匹配,因此指针将位于y

的末尾
Mary had a little lamb
    ^

在下一部分(\s[A-Za-z]+)中,它将匹配一个空格,后跟另一个单词,所以......

Mary had a little lamb
        ^

指针将是单词had结束的位置。所以这就是你的问题,你正在增加正则表达式的内部指针而不想要,这是如何解决的? Lookaround是你的朋友。使用lookarounds(lookahead和lookbehind),您可以在不增加正则表达式的主内部指针的情况下遍历文本(它将使用另一个指针)。

所以最后,符合你想要的正则表达式是:([A-Za-z]+(?=\s[A-Za-z]+))

说明:

唯一认为你不知道正则表达式是(?=\s[A-Za-z]+)部分,这意味着[A-Za-z]+必须后跟一个单词,否则正则表达式不会匹配。这正是你想要的东西,因为内部指针不会增加并且会匹配每一个字但是最后一个,因为最后一个字不会跟着一个字。

然后,一旦你有了,你只需要替换你现在所做的一切。

这里有一个工作示例,DEMO

答案 5 :(得分:0)

完全钦佩'预见'的概念,我仍然提出pairwise函数(demo),因为它真的是Regex的任务来标记字符流,以及决定什么与令牌有关的是业务逻辑。至少,这是我的意见。

遗憾的是Javascript还没有成对,但这可以做到:

function pairwise(a, f) {
  for (var i = 0; i < a.length - 1; i++) {
     f(a[i], a[i + 1]);
  }
}

var str = "Mary had a little lamb";

pairwise(str.match(/\w+/g), function(a, b) {
  document.write("<br>"+a+" "+b);
});

​