将带有HTML的句子拆分为单词(但保留内联HTML完整)

时间:2014-11-09 17:37:38

标签: javascript html regex space preg-split

我正在寻找一种方法来使用javascript将带有HTML的句子拆分为单词,并保留带有文本内容的内嵌HTML标记。标点符号可以被视为它最接近的单词的一部分。我想使用正则表达式,可能preg_split()来分割句子。以下是一个例子:

A word, <a href='#' title=''>words within tags should remain intact</a>, so here's
<b>even more</b> <u>words</u>

最好,我想最终得到以下结论:

[0] => A
[1] => word,
[2] => <a href='#' title=''>words within tags should remain intact</a>,
[3] => so
[4] => here's
[5] => <b>even more</b>
[6] => <u>words</u>

我知道有关使用Regex解析HTML的讨论(我喜欢阅读Bobince' answer :- P),但我需要拆分句子的单词而不会损坏带有属性的html标签。我不知道如何使用JS以与Regex不同的方式来实现这一点。当然,如果有其他选择,我会非常乐意调整它们,以达到类似的效果。 : - )


修改 我在Stackoverflow上搜索了类似的问题,但这些问题并没有为我打勾。稍微透视一下:

2 个答案:

答案 0 :(得分:2)

这是可能的,但使用纯正则表达式解决方案会有一些缺点。最容易调用的是嵌套HTML。我即将展示的解决方案使用一些反向引用来试图解决这个问题,但是如果你得到一些复杂的嵌套HTML,它可能会以奇怪的方式开始失败。

/(?:<(\w+)[^>]*>(?:[\w+]+(?:(?!<).*?)<\/\1>?)[^\s\w]?|[^\s]+)/g

Regex Demo

正则表达式使用反向引用和负面外观来完成工作。您可以根据需要删除后退参考。后引用有助于支持嵌套标记。

JSFiddler Example - 检查控制台输出中的示例。

这是JS Fiddler的输出(我稍微格式化了输出)

[
  "A", 
  "word,", 
  "<a href='#' title=''>words within tags should remain intact</a>,", 
  "so", 
  "here's", 
  "<b>even more</b>", 
  "<u>words</u>"
] 

根据您的使用情况,您需要对其进行修改才能使用。我考虑过任何不是空格的词,但你可能有不同的标准。

此方法的一个不利之处是,如果起始HTML标记位于单词的末尾,则无法正确拾取。即。 test<span>something else</span>

答案 1 :(得分:1)

您可以使用以下代码段:

&#13;
&#13;
function splitIntoWords(div) {
  function removeEmptyStrings(k) {
    return k !== '';
  }
  var rWordBoundary = /[\s\n\t]+/; // Includes space, newline, tab
  var output = [];
  for (var i = 0; i < div.childNodes.length; ++i) { // Iterate through all nodes
    var node = div.childNodes[i];
    if (node.nodeType === Node.TEXT_NODE) { // The child is a text node
      var words = node.nodeValue.split(rWordBoundary).filter(removeEmptyStrings);
      if (words.length) {
        output.push.apply(output, words);
      }
    } else if (node.nodeType === Node.COMMENT_NODE) {
      // What to do here? You can do what you want
    } else {
      output.push(node.outerHTML);
    }
  }
  return output;
}

window.onload = function() {
  var div = document.querySelector("div");
  document.querySelector("pre").innerText = 'Output: ' + JSON.stringify(splitIntoWords(div));
}
&#13;
<!-- Note you have to surround your html with a div element -->
<div>A word, <a href='#' title=''>words within tags should remain intact</a>, so here's
  <b>even more</b>  <u>words</u>
</div>
<pre></pre>
&#13;
&#13;
&#13;

它遍历所有子节点,获取文本节点并将它们分成单词(您可以安全地执行此操作,因为文本节点不能包含子节点)。

这可以解决大多数问题。有了这个,text<span>Test</span>之类的HTML将会出现["text", "<span>Test</span>"],与上面的答案不同。

这可能会导致<span>There are</span>: 4 words失败,导致["<span>There are</span>", ":" /* Extra colon */, "4", "words"](它应该这样做,但不确定是否合适)。

我认为嵌套元素非常安全。