JavaScript正则表达式排除+包含模式匹配

3

我正在使用JavaScript RegExp来在HTML内容中进行搜索高亮。

为此,我使用以下方法:

data.replace( new RegExp("("+search+")", 'g'), "<b id='searchHighlight'>$1</b>" );

其中data代表整个HTML内容,search代表搜索字符串。

当搜索例如h时,它会在单词中(the,there等)以及标签中的实例(例如"<h1 id="title"> Something </h1>")中突出显示h。

我不能采用其他方法,因为我需要用相同的样式突出显示相同的HTML内容。

我已经阅读了一些解决方案:

var input = "a dog <span class='something'> had a  </span> and a cat";
// Remove anything tag-like
var temp = input.replace(/<.+?>/g, "");
// Perform the search
var matches = new RegExp(exp, "g").exec(temp);

但是由于我需要在同一HTML内容中突出显示搜索文本,所以我不能简单地剥去现有的标签。 是否有办法在RegExp中执行包含和排除搜索,以便我可以例如在"the"中突出显示h"t<b id='searchHighlight'>h</b>e",并防止"<h1 id="title">Test</h1>"被破坏,因此:"<<b id='searchHighlight'>h</b>1 id="title">Test</<b id='searchHighlight'>h</b>1>"

HTML内容是静态的,并且看起来像这样:

    <h1 id="title">Samples</h1>
        <div id="content">
            <div  class="principle">
        <h2 id="heading">           
            PRINCIPLE</h2>


        <p>
            FDA recognizes that samples are an important part of ensuring that the right drugs are provided to the right patients. Under the Prescription Drug Marketing Act (PDMA), a sales representative is permitted to provide prescription drug samples to eligible healthcare professionals (HCPs). In order for BMS to provide this service, representatives must strictly abide by all applicable compliance standards pertaining to the distribution of samples.</p></div>
<h2 id="heading">           
            WHY DOES IT MATTER?</h2>
        <p>
            The Office of Inspector General (OIG) recognizes that samples can have monetary value to HCPs and, when used improperly, may have implications under the Federal False Claims Act and the Federal Anti-kickback Act. To minimize risk of such liability, the OIG requires the clear and conspicuous labeling of individual samples as units that cannot be sold.&nbsp; BMS and its business partners label every sample package to meet this requirement.&nbsp; Additionally, the HCP signature statement acknowledges that the samples will not be sold, billed or provided to family members or friends.</p>
        <h2 id="heading">

            WHO IS YOUR SMaRT PARTNER?</h2>
        <p>
            SMaRT is an acronym for &ldquo;Samples Management and Representatives Together&rdquo;.&nbsp; A SMaRT Partner has a thorough understanding of BMS sample requirements and is available to assist the field with any day-to-day policy or procedure questions related to sample activity. A SMaRT Partner will also:</p>

        <ul>
            <li style="margin-left:22pt;"> Monitor your adherence to BMS&rsquo;s sample requirements.</li>
            <li style="margin-left:22pt;"> Act as a conduit for sharing sample compliance issues and best practices.</li>
            <li style="margin-left:22pt;"> Respond to day-to-day sample accountability questions within two business days of receipt.</li>
        </ul>
        <p>

            Your SMaRT Partner can be reached at 888-475-2328, Option 3.</p>
        <h2 id="heading">

            BMS SAMPLE ACCOUNTABILITY POLICIES &amp; PROCEDURES</h2>
        <p>
            It is the responsibility of each sales representative to read, understand and follow the BMS Field Sample Accountability Procedures, USPSM-SOP-101. The basic expectations are:</p>
        <ul>
            <li style="margin-left:22pt;"> Transmit all sample activity by communicating your tablet to the host server on a <strong>daily</strong> basis.</li>
            <li style="margin-left:22pt;"> Maintain a four to six week inventory of samples rather than excessive, larger inventories that are more difficult to manage and increase your risk of non-compliance.</li>
            <li style="margin-left:22pt;"> Witness all HCP&rsquo;s signatures to confirm request and receipt of samples.</li>
        </ul>
</div>

这些内容都是分散的,不在一个标签内。因此,DOM操作对我来说不是一个解决方案。


[^<]h 可以排除 <h,但最好使用 DOM 操作来实现。 - QuentinUK
3个回答

4

如果您确定标签属性中没有<>字符,您可以直接使用以下方法:

data = data.replace( 
    new RegExp( "(" + search + "(?![^<>]*>))", 'g' ),
        "<b id='searchHighlight'>$1</b>" );

负向先行断言(?![^<>]*>)可以避免在字符串中出现>之前的<,因为它会出现在标签内部。这远非绝对可靠,但可能已经足够了。
顺便说一下,由于你正在进行全局匹配,即进行多个替换,id='searchHighlight' 应该改为 class='searchHighlight'
并且你需要小心,确保search不包含任何正则表达式特殊字符。

我认为这只会检查search字符串是否紧随其后<>...至少这就是我理解?!量词的方式。不过关于idclass的观点很好。 - guypursey
@guypursey。那是不正确的,(?!) 是一个负向先行断言而不是量词。[^<>}* 将匹配任何不是 <> 的字符。 - MikeM
谢谢你,Mike。你救了我的一天。我没有足够的声望来投票支持你的帖子。等我有了声望,我会去投票的 :) 再次感谢。 - leninmon

1

你可能已经意识到,你试图使用错误的工具来完成工作,所以这只是为了记录(如果你没有意识到,你可以找到这个有启发性的链接)。

在基本上任意文本内容的HTML属性中,你可能会遇到一个根本性问题,即title(工具提示属性)和data-...(通用用户定义属性通过设计保存任意数据)- 无论你在HTML代码的文本部分中找到什么,你也可以在那里找到替换,这将破坏气球帮助和/或破坏一些应用程序逻辑。还要注意,文本内容中的任何字符都可以编码为命名实体或数字实体(例如& -> &amp;&#x26;&#38;),原则上可以处理,但会使动态正则表达式复杂化(如果你的变量search将保存直接文本,则大大增加)。

说了这么多,你也许可以使用data.replace( new RegExp("([>]?)[^><]*("+search+")[^><]*([<]?)", 'g'), "<b id='searchHighlight'>$1$2$3</b>" );,除非需要突出显示的搜索结果可能包含在正则表达式规范中具有语义的字符,例如.+*|([{}])\,或者-;这些您必须适当转义。

总之: 修改您的设计以避免很多麻烦

顺便问一下,为什么不选择dom遍历?您不需要知道实际存在的html标签来做到这一点。


感谢collapsar的提示。我喜欢dom遍历的想法,但问题在于,为了设置样式,我要么必须获取innerHTML并更改它,要么获取innerText并更改它。 但更改innerHtml将产生相同的结果(更改html标记)。 而更改innerText将按原样显示高亮标记(例如:t<b>h</b>e)。 - leninmon
你根本不需要使用 innerHTML。DOM 将文本存储在其自己类型的节点中,这些节点不包含任何 HTML 标记。因此,您需要遍历 DOM,将相邻文本节点的内容收集到一个字符串中,执行替换操作,按标记开始和结束位置拆分生成的字符串(这里与原始设计的关键区别在于:您知道存在的唯一标记是您在替换期间插入的标记),并将部分作为一系列文本节点和 <b> 节点重新插入以替换原始节点。所有功能都可在 DOM 中使用... - collapsar
当然,由于您无论如何都会为突出显示创建dom节点,因此您的替换字符串可以是简单的文本,标记突出显示部分的开头和结尾。在遇到highlight_start时,您将创建一个新的dom节点<b>,其中包含数组的下一个元素作为子文本节点,并跳过highlight_end标记,否则您只需创建一个文本节点。 - collapsar

0

这不是一个纯正的RegExp解决方案,但如果您无法遍历DOM,则可以使用函数替换和循环进行字符串操作,就像这样。

  1. 声明所需变量并获取文档主体的innerHTML。
  2. 查看数据,提取任何标记并将它们保存在数组中。现在先留下一个占位符,以便知道在哪里放回它们。
  3. 在字符串中用临时占位符替换所有标记后,您可以使用原始代码替换所需的字符,但将结果分配回data
  4. 然后您需要通过反转早期过程来恢复标记。
  5. 将新的data分配为文档主体的innerHTML。

这是该过程的实际应用

以下是代码:

var data = document.body.innerHTML, // get the DOM as a string
    tagarray = [], // a place to temporarily store all your tags
    tagmatch = /<[^>]+>/g, // for matching tags
    tagplaceholder = '<>', // could be anything but should not match the RegExp above, and not be the same as the search string below
    search = 'h'; // for example; but this could be set dynamically

while (tagmatch.test(data)) {
    data = data.replace(tagmatch, function (str) {
        tagarray.push(str); // store each matched tag in your array
        return tagplaceholder; // whatever your placeholder should be
    });
}

data = data.replace( new RegExp("("+search+")", 'g'), "<b id='searchHighlight'>$1</b>" ); // now search and replace the string of your choice

while (new RegExp(tagplaceholder, 'g').test(data)) {
    data = data.replace(tagplaceholder, function (str) {
        return tagarray.shift(str); // replace the placeholders with the tags you saved earlier to restore them
    });
}

document.body.innerHTML = data; // assign the changed `data` string to the body

显然,如果你能把这一切放在自己的函数中,那就更好了,因为你不想让像上面那样的全局变量挂在那里。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接