在 Javascript 中返回正则表达式 match() 的位置?

Return positions of a regex match() in Javascript?

提问人:stagas 提问时间:2/19/2010 更新时间:8/11/2022 访问量:186933

问:

有没有办法在 Javascript 中检索正则表达式 match() 结果字符串中的(起始)字符位置?

JavaScript 正则表达式 字符串匹配

评论


答:

304赞 Gumbo 2/19/2010 #1

exec 返回一个具有属性的对象:index

var match = /bar/.exec("foobar");
if (match) {
    console.log("match found at " + match.index);
}

对于多场比赛:

var re = /bar/g,
    str = "foobarfoobar";
while ((match = re.exec(str)) != null) {
    console.log("match found at " + match.index);
}

评论

6赞 stagas 2/19/2010
感谢您的帮助!您能告诉我如何找到多个匹配项的索引吗?
31赞 oriadam 12/24/2015
注意:使用 the 作为变量和添加修饰符都至关重要!否则,您将获得无休止的循环。reg
1赞 Jimbo Jonny 3/30/2016
@OnurY ıldırım - 这是它工作的 jsfiddle......我已经一直测试到IE5了......效果很好:jsfiddle.net/6uwn1vof
1赞 Onur Yıldırım 3/30/2016
@JimboJonny,嗯,我学到了一些新东西。我的测试用例返回 .jsfiddle.net/6uwn1vof/2 这不是一个像你这样的类似搜索的例子。undefined
1赞 Jimbo Jonny 3/30/2016
@OnurY ıldırım - 删除标志,它就会起作用。由于是字符串的函数,而不是正则表达式,因此它不能像 一样是有状态的,因此如果您不寻找全局匹配,它只会将其视为(即具有索引属性)......因为这样一来,状态就无关紧要了。gmatchexecexec
10赞 Jimmy 2/19/2010 #2

您可以使用对象的方法。这仅适用于第一个匹配项,但会执行您描述的操作。例如:searchString

"How are you?".search(/are/);
// 4
78赞 stagas 2/19/2010 #3

这是我想出的:

// Finds starting and ending positions of quoted text
// in double or single quotes with escape char support like \" \'
var str = "this is a \"quoted\" string as you can 'read'";

var patt = /'((?:\\.|[^'])*)'|"((?:\\.|[^"])*)"/igm;

while (match = patt.exec(str)) {
  console.log(match.index + ' ' + patt.lastIndex);
}

评论

25赞 Beni Cherniavsky-Paskin 6/6/2013
match.index + match[0].length也适用于终端位置。
1赞 David 5/20/2015
@BeniCherniavsky-帕斯金,最终位置不是吗?match.index + match[0].length - 1
2赞 Beni Cherniavsky-Paskin 5/20/2015
@David,我的意思是排他性的结束位置,例如由 和 .正如您所说,包容性结束将少 1 个。(请注意,包含通常意味着匹配中最后一个字符的索引,除非它是空匹配,在匹配为 1,并且可能在开始时完全在字符串之外用于空匹配.......slice().substring()-1
0赞 abinas patra 4/26/2021
因为它进入了无限循环,我们如何限制它?patt = /.*/
2赞 Sandro Rosa 9/26/2015 #4

此成员 fn 返回 String 对象中输入字的从 0 开始的位置(如果有)的数组

String.prototype.matching_positions = function( _word, _case_sensitive, _whole_words, _multiline )
{
   /*besides '_word' param, others are flags (0|1)*/
   var _match_pattern = "g"+(_case_sensitive?"i":"")+(_multiline?"m":"") ;
   var _bound = _whole_words ? "\\b" : "" ;
   var _re = new RegExp( _bound+_word+_bound, _match_pattern );
   var _pos = [], _chunk, _index = 0 ;

   while( true )
   {
      _chunk = _re.exec( this ) ;
      if ( _chunk == null ) break ;
      _pos.push( _chunk['index'] ) ;
      _re.lastIndex = _chunk['index']+1 ;
   }

   return _pos ;
}

现在试试

var _sentence = "What do doers want ? What do doers need ?" ;
var _word = "do" ;
console.log( _sentence.matching_positions( _word, 1, 0, 0 ) );
console.log( _sentence.matching_positions( _word, 1, 1, 0 ) );

您还可以输入正则表达式:

var _second = "z^2+2z-1" ;
console.log( _second.matching_positions( "[0-9]\z+", 0, 0, 0 ) );

这里得到线性项的位置指数。

27赞 Jimbo Jonny 3/30/2016 #5

developer.mozilla.org String 方法的文档中:.match()

返回的 Array 有一个额外的输入属性,其中包含 已分析的原始字符串。此外,它还有一个索引 属性,表示 字符串

当处理非全局正则表达式(即,正则表达式上没有标志)时,返回的值有一个属性...您所要做的就是访问它。g.match()index

var index = str.match(/regex/).index;

下面是一个示例,显示它也起作用:

var str = 'my string here';

var index = str.match(/here/).index;

console.log(index); // <- 10

我已经成功地测试了这一点,一直到IE5。

评论

0赞 Ben Taliadoros 1/21/2022
这将返回一个数组,而不是一个带有索引的对象
0赞 phil294 3/24/2022
@BenTaliadoros 恐怕你错了,它是数组,又是具有属性的对象(见答案)index
1赞 Ben Taliadoros 3/24/2022
好像是这样!不知道我多年前在想什么
2赞 SethWhite 11/5/2022
请注意,如果使用 str.match(/here/g) 执行全局标志,则 match.index 将未定义。
2赞 Yaroslav 6/27/2016 #6
var str = "The rain in SPAIN stays mainly in the plain";

function searchIndex(str, searchValue, isCaseSensitive) {
  var modifiers = isCaseSensitive ? 'gi' : 'g';
  var regExpValue = new RegExp(searchValue, modifiers);
  var matches = [];
  var startIndex = 0;
  var arr = str.match(regExpValue);

  [].forEach.call(arr, function(element) {
    startIndex = str.indexOf(element, startIndex);
    matches.push(startIndex++);
  });

  return matches;
}

console.log(searchIndex(str, 'ain', true));

评论

0赞 rakslice 4/15/2019
这是不正确的。 这里只是找到匹配捕获的文本的下一个出现,不一定是匹配。JS 正则表达式支持捕获之外的文本条件,并具有 lookahead。例如,应该给出 ,而不是 。str.indexOfsearchIndex("foobarfoobaz", "foo(?=baz)", true)[6][0]
0赞 Ankit Kumar 7/23/2019
为什么 ' [].forEach.call(arr, function(element)' 为什么不 arr.forEach 或 arr.map
7赞 felipeab 10/30/2016 #7

这是我最近发现的一个很酷的功能,我在控制台上尝试了这个,它似乎有效:

var text = "border-bottom-left-radius";

var newText = text.replace(/-/g,function(match, index){
    return " " + index + " ";
});

返回:“边界 6 底部 13 左 18 半径”

所以这似乎是你要找的。

评论

6赞 Mike 'Pomax' Kamermans 2/26/2017
请注意,替换函数也会添加捕获组,因此请注意,替换函数中倒数第二个条目始终是位置。不是“第二个论点”。函数参数为“full match, group1, group2, ...., index of match, full string matched tost”arguments
0赞 SwiftNinjaPro 12/13/2019 #8
function trimRegex(str, regex){
    return str.substr(str.match(regex).index).split('').reverse().join('').substr(str.match(regex).index).split('').reverse().join('');
}

let test = '||ab||cd||';
trimRegex(test, /[^|]/);
console.log(test); //output: ab||cd

function trimChar(str, trim, req){
    let regex = new RegExp('[^'+trim+']');
    return str.substr(str.match(regex).index).split('').reverse().join('').substr(str.match(regex).index).split('').reverse().join('');
}

let test = '||ab||cd||';
trimChar(test, '|');
console.log(test); //output: ab||cd
31赞 brismuth 7/10/2020 #9

在现代浏览器中,你可以使用 string.matchAll() 来实现这一点。

这种方法的好处是,它不依赖于正则表达式是有状态的,就像@Gumbo的回答一样。RegExp.exec()

let regexp = /bar/g;
let str = 'foobarfoobar';

let matches = [...str.matchAll(regexp)];
matches.forEach((match) => {
    console.log("match found at " + match.index);
});

评论

1赞 Steven Schkolne 6/16/2022
我很幸运地使用了这个基于 ''' let regexp = /bar/g 的单行解决方案;let str = 'foobarfoobar';let matchIndices = Array.from(str.matchAll(regexp)).map(x => x.index);控制台.log(matchIndices)'''matchAll
0赞 Ooker 7/10/2023
不知道为什么你说这种方法不依赖于正则表达式是有状态的。我尝试您的代码没有标志并得到错误g
0赞 brismuth 7/11/2023
“g”标志表示“全局搜索”,即匹配字符串中出现的所有内容。如果您不进行全局搜索,则使用 str.matchAll() 是没有意义的。希望这会有所帮助,但我不确定你想做什么。对于我的“有状态”注释,我的意思是您不必使用“while”循环并依赖正则表达式对象的内部状态,就像我链接的 Gumbo 的答案一样。祝你好运!
-1赞 Thomas FONTAINE 12/22/2020 #10

var str = 'my string here';

var index = str.match(/hre/).index;

alert(index); // <- 10

评论

3赞 Andreas 12/22/2020
所以就像 4 年前的这个答案一样(与你的不同,有效)
4赞 Claude 6/12/2021 #11

恐怕前面的答案(基于)似乎不起作用,以防您的正则表达式与宽度 0 匹配。例如(注意:是应该找到所有单词边界的正则表达式):exec/\b/g

var re = /\b/g,
    str = "hello world";
var guard = 10;
while ((match = re.exec(str)) != null) {
    console.log("match found at " + match.index);
    if (guard-- < 0) {
      console.error("Infinite loop detected")
      break;
    }
}

可以尝试通过让正则表达式匹配至少 1 个字符来解决这个问题,但这远非理想(这意味着您必须在字符串末尾手动添加索引)

var re = /\b./g,
    str = "hello world";
var guard = 10;
while ((match = re.exec(str)) != null) {
    console.log("match found at " + match.index);
    if (guard-- < 0) {
      console.error("Infinite loop detected")
      break;
    }
}

一个更好的解决方案(仅适用于较新的浏览器/需要在较旧的/IE 版本上使用 polyfill)是使用 String.prototype.matchAll()

var re = /\b/g,
    str = "hello world";
console.log(Array.from(str.matchAll(re)).map(match => match.index))

解释:

String.prototype.matchAll() 需要一个全局正则表达式(一个设置了全局标志的正则表达式)。然后,它返回一个迭代器。为了循环和迭代器,它必须变成一个数组(这正是这样做的)。与 的结果一样,生成的元素具有根据规范的字段。gmap()Array.from()RegExp.prototype.exec().index

请参阅 String.prototype.matchAll() 和 Array.from() MDN 页面,了解浏览器支持和 polyfill 选项。


编辑:更深入地寻找所有浏览器都支持的解决方案

问题在于它更新了正则表达式上的指针,下次从之前找到的 .RegExp.prototype.exec()lastIndexlastIndex

var re = /l/g,
str = "hello world";
console.log(re.lastIndex)
re.exec(str)
console.log(re.lastIndex)
re.exec(str)
console.log(re.lastIndex)
re.exec(str)
console.log(re.lastIndex)

只要正则表达式匹配实际上具有宽度,这就可以很好地工作。如果使用 0 宽度的正则表达式,则此指针不会增加,因此您将获得无限循环(注意:是 l 的展望 - 它与 .因此,它在第一次调用 时正确地转到索引 2,然后停留在那里:/(?=l)/glexec()

var re = /(?=l)/g,
str = "hello world";
console.log(re.lastIndex)
re.exec(str)
console.log(re.lastIndex)
re.exec(str)
console.log(re.lastIndex)
re.exec(str)
console.log(re.lastIndex)

因此,解决方案(不如matchAll()好,但应该适用于所有浏览器)是如果匹配宽度为0(可以用不同的方式检查),则手动增加lastIndex

var re = /\b/g,
    str = "hello world";
while ((match = re.exec(str)) != null) {
    console.log("match found at " + match.index);

    // alternative: if (match.index == re.lastIndex) {
    if (match[0].length == 0) {
      // we need to increase lastIndex -- this location was already matched,
      // we don't want to match it again (and get into an infinite loop)
      re.lastIndex++
    }
}

1赞 Steven Schkolne 6/16/2022 #12

我很幸运地使用了这个基于(我的用例需要字符串位置数组)的单行解决方案matchAll

let regexp = /bar/g;
let str = 'foobarfoobar';

let matchIndices = Array.from(str.matchAll(regexp)).map(x => x.index);

console.log(matchIndices)

输出: [3, 9]