197

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

4

11 回答 11

274

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);
}

于 2010-02-19T10:49:16.187 回答
71

这是我想出的:

// 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);
}

于 2010-02-19T11:38:26.793 回答
20

在现代浏览器中,您可以使用string.matchAll()完成此操作。

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

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

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

于 2020-07-09T21:50:25.247 回答
18

来自关于 String方法的developer.mozilla.org文档:.match()

返回的 Array 有一个额外的输入属性,其中包含已解析的原始字符串。此外,它还有一个 index 属性,表示字符串中匹配项的从零开始的索引

在处理非全局正则表达式(即,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。

于 2016-03-29T22:44:39.803 回答
9

您可以使用对象的search方法String。这仅适用于第一场比赛,但会按照您的描述进行。例如:

"How are you?".search(/are/);
// 4
于 2010-02-19T10:51:37.760 回答
6

这是我最近发现的一个很酷的功能,我在控制台上尝试过,它似乎可以工作:

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

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

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

所以这似乎是你正在寻找的。

于 2016-10-30T13:39:25.277 回答
2

如果您的正则表达式匹配宽度 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() 需要一个全局正则表达式(带有g全局标志集的一个)。然后它返回一个迭代器。为了循环和map()迭代器,它必须变成一个数组(这正是这样Array.from()做的)。与 的结果一样,结果RegExp.prototype.exec()元素具有.index符合规范的字段。

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


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

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

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)/g是 l 的前瞻 - 它与 . 之前的 0 宽度字符串匹配l。所以它在第一个正确地转到索引 2调用exec(),然后停留在那里:

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++
    }
}

于 2021-06-12T10:22:39.550 回答
1

此成员 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 ) );

这里得到线性项的位置索引。

于 2015-09-25T16:40:28.673 回答
1
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));
于 2016-06-27T11:17:13.017 回答
-1

var str = 'my string here';

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

alert(index); // <- 10

于 2020-12-22T12:53:03.527 回答
-1
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
于 2019-12-12T18:40:43.580 回答