匹配一个单词,其中候选人可以跨越顺序组(跨度)



用户将提供一些搜索词。假设它是一个字符串,可以包含任何单词或特殊字符(如/?,$*等)。

我需要在HTML中出现的任何地方匹配这个字符序列,即使搜索词跨越了顺序跨度;在我的HTML中,特殊字符有时被单独包装。

例如:用户提供"你妈妈?",有一段包含<span>Your mom</span><span class="special">?</span>

我需要一种有效的方法来确定a)查询确实存在,以及b)哪些元素包含查询。搜索到的文本可以是复杂的HTML,并包含大量的单词、跨度、div等。

我可能会尝试编写一个解析器,它可以区分打开和关闭标记及其文本内容(希望HTML中不包含不完整的标记)。对于索引,也许可以使用元组堆栈,每个元组表示深度和计数,以及当前状态的内存。您的简单示例将索引为:

[(1,1)] tag opens, text: 'Your mom'
query text matches so far
[(1,1),(1,1)] tag closes, remove. 
[(1,2)] tag opens, maintain depth, increase count, text: '?'
query text continues to match
[(1,2)] tag closes, remove

您首先必须将"字符"分组。最偏执的方法是通过性格来做到这一点,但最终会非常低效。我知道我对您的数据所做的很少,我假设任何与[a-zA-Zs]+匹配的东西都会变成一个令牌,而其他的东西则会变成另一个令牌。

另一件合乎逻辑的事情是做一个迭代过程,在每次尝试失败后,你都会进一步分解它。

无论你做什么决定,你都需要使用一些JavaScript来实现这一点。但这应该很容易做到

拆分后,您需要开始考虑构建正则表达式。

您可以在每个令牌之间放置(?:<[^>]*>s*)*,但在将某些字符放入regex之前,需要对其进行转义。某个地方有一个完整的列表,但其中包括:$^*.+?/{}[]()

举个例子,你可能会得到这样的结果:

/your mom(?:<[^>]*>s*)*?/i

i表示不区分大小写。

你可以这样得到匹配位置的索引:

var match = /regex/.exec("string to match against");
if (match) {
    alert("match found at " + match.index);
}

此解决方案将查找并返回包含搜索文本的第一个元素,即使该文本包含嵌入的标记。

TL;DR以身作则!

var content = $("#content");
var search = $("#search");
var go = $("#go");
function escapeRegExp(str) {
  return str.replace(/[/\{}()*+?.^$|[]-]/g, "\$&");
}
function recursiveElementSearch(regex, element) {
  var text = element.text();
  
  if (text.match(regex)) {
    var children = element.children();
    var len = children.length;
    
    for (var i=0; i < len; ++i) {
      var child = $(children[i]);
      var found = recursiveElementSearch(regex, child);
      
      if (found != null) {
        return found;
      }
    }
    
    return element;
  }
  
  return null;
}
go.click(function() {
  var value = $.map(search.val().split(""), function(value, index) {
    return escapeRegExp(value);
  });
  var regex = new RegExp(value.join(""), "i");
  var element = recursiveElementSearch(regex, content);
  console.log("Element: ", element ? element.attr("id") : "null");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="content">
  <div id="first">
    <span id="a">Your mom</span><span class="special">?</span>
  </div>
  <div id="second">
    <span id="b">Where is <strong>your</strong> mom</span><span class="special">?</span>
  </div>
  <div id="third">
    <span id="c">Yours<span>&nbsp;</span><a href="#">mom</a></span><span class="special">?</span>
  </div>
  <div id="fourth">
    <span id="d">My mom</span><span class="special">!</span>
  </div>
  <div id="fifth">
    <span id="e">Their mom<i>s</i></span><span class="special">...</span>
  </div>
</div>
<input id="search" value="Your mom?">
<label for="search">Search:</label>
<button id="go">Go!</button>

其工作方式是对输入文本进行净化(转义),然后递归检查每个元素的文本,以查看是否包含搜索文本。

将返回的元素是在最深级别找到的第一个元素。搜索是深度优先的,因此在第一个元素的3级深度上找到的匹配将在第二个元素的1级深度之前返回。

提供的HTML片段表明嵌套标签不是问题。使用此HTML,对"Your mom?"的搜索结果返回div id="first",对"mom!"的搜索返回div id="fourth",而对"Yours mom"的搜索则返回div id="c"

可以做一些简单的改进。以下是我从测试中看到的有用内容:

  • 折叠搜索文本中的空格以匹配任意数量的空格(例如,搜索"Yours mom"应与搜索"Your mom"相同)
  • 处理Unicode空格,包括"nbsp;"
  • 包括一个返回所有匹配项的版本(例如,搜索"你妈妈?"应返回[div id="first"div id="second"],因为两者都匹配)

考虑到所有这些,这是一种在页面上搜索文本的非常有用的方法。

最新更新