【问题标题】:Return positions of a regex match() in Javascript?返回 Javascript 中正则表达式 match() 的位置?
【发布时间】:2011-01-18 17:37:39
【问题描述】:

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

【问题讨论】:

    标签: javascript regex match string-matching


    【解决方案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);
    }

    【讨论】:

    • 感谢您的帮助!你能告诉我如何找到多个匹配项的索引吗?
    • 注意:使用re作为变量,加上g修饰符都是至关重要的!否则会陷入死循环。
    • @OnurYıldırım - 这是它的一个 jsfiddle 工作......我已经测试它一直回到 IE5......工作得很好:jsfiddle.net/6uwn1vof
    • @JimboJonny,嗯,我学到了一些新东西。我的测试用例返回undefinedjsfiddle.net/6uwn1vof/2 这不是像你这样的类似搜索的例子。
    • @OnurYıldırım - 删除 g 标志,它会工作。由于match 是字符串的函数,而不是正则表达式,它不能像exec 那样是有状态的,因此如果您不寻找全局匹配,它只会将其视为exec(即具有索引属性)。 ..因为有状态并不重要。
    【解决方案2】:

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

    "How are you?".search(/are/);
    // 4
    

    【讨论】:

      【解决方案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);
      }

      【讨论】:

      • match.index + match[0].length 也适用于结束位置。
      • 真的很好 - comparison gisted here
      • @BeniCherniavsky-Paskin,结束位置不是match.index + match[0].length - 1吗?
      • @David,我的意思是排他的结束位置,例如通过.slice().substring()。正如你所说,包容性结束会少1。 (请注意,包含通常意味着匹配中最后一个字符的索引,除非它是一个空匹配,其中它是 1 个 before 匹配并且可能在字符串之外完全是 -1 以在开始时为空匹配......)
      • 对于patt = /.*/ 它进入无限循环我们如何限制它?
      【解决方案4】:

      这个成员 fn 返回一个从 0 开始的位置数组,如果有的话,在 String 对象中的输入单词

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

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

      【讨论】:

        【解决方案5】:

        来自developer.mozilla.org 字符串.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。

        【讨论】:

          【解决方案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));
          

          【讨论】:

          • 这是不正确的。 str.indexOf这里只是找到匹配捕获的文本的下一个出现,不一定是匹配。 JS 正则表达式支持使用前瞻捕获捕获之外的文本条件。例如searchIndex("foobarfoobaz", "foo(?=baz)", true) 应该给[6],而不是[0]
          • 为什么 ` [].forEach.call(arr, function(element)` 为什么不是 arr.forEach 或 arr.map
          【解决方案7】:

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

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

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

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

          【讨论】:

          • 请注意替换函数也会添加捕获组,因此请注意替换函数arguments 中的倒数第二个 条目始终是位置。不是“第二个论点”。函数参数是“完全匹配,组1,组2,....,匹配索引,匹配的完整字符串”
          【解决方案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
          

          【讨论】:

            【解决方案9】:

            在现代浏览器中,您可以使用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);
            });

            【讨论】:

              【解决方案10】:

              var str = 'my string here';
              
              var index = str.match(/hre/).index;
              
              alert(index); // <- 10

              【讨论】:

              • 就像 4 年前的 this answer 一样(与你的不同,它有效)
              【解决方案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() 需要一个全局正则表达式(带有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 的前瞻——它匹配 l 之前的 0 宽度字符串。所以它正确运行在第一次调用 exec() 时索引 2,然后停留在那里:

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

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2022-07-28
                • 1970-01-01
                • 2016-03-30
                • 2014-06-01
                • 1970-01-01
                相关资源
                最近更新 更多