【问题标题】:PCRE REGEX to match one or more sentences containing a set of charactersPCRE REGEX 匹配一个或多个包含一组字符的句子
【发布时间】:2021-06-13 04:22:00
【问题描述】:

我的文本块仅包含 一个 特定的 HTML 标记(即“标记”标记),我想提取包含该标记的所有连续“句子”的一段。我的用例中的“句子”由问号、感叹号、句号或分号分隔。

编辑:“标记”标签是在服务器端自动生成的,它们总是格式正确的。在我的用例中没有召唤克苏鲁的风险。

我尝试过的:

this PCRE regex 中的第二个结果开始,它适用于选择所有包含单词“flung”的句子,例如 this regex tester。我添加了分号,因为它们也在我的用例中:

/[^.;?!]*(?<=[.;?\s!])flung(?=[\s.;?!])[^.;?!]*[.;?!]/igm

这很好用,除了两个我仍然需要帮助的问题:

  • 如何排除十进制数字,例如12.34 比赛期间? “Lorem ipsum 12.34 dolor flung sat amet”应该是一句话。目前,它将十进制数字中的句点作为标点符号,但事实并非如此。我想修改 REGEX 以检测小数点周围是否有数字或字母会起作用,但我尝试了诸如?:[^\.]|\.(?=\d) 之类的前瞻约束,但它不匹配,或者我没有这样做对。

  • 我想修改它以匹配所有“标记”HTML 标记,而不是诸如“flung”之类的词。我知道 REGEX 不适合 html 标记,但是 HTML 解析器也无法识别这些字符(?!。;)。也许我可以考虑将两者结合起来?

我的期望:

示例 1:(基本匹配)

harum quidem rerum facilis est et expedita distinctio? Nam libero tempore, cum soluta nobis est eligendi optio &lt;mark&gt;cumque&lt;/mark&gt; nihil impedit .23 quo minus id 0.89 quod maxime placeat facere possimus, omnis voluptas assumenda est, 12.34 omnis dolor repellendus! Itaque earum rerum hic tenetur a sapiente delectus, quod maxime placeat

应该返回

Nam libero tempore, cum soluta nobis est eligendi optio &lt;mark&gt;cumque&lt;/mark&gt; nihil impedit .23 quo minus id 0.89 quod maxime placeat facere possimus, omnis voluptas assumenda est, 12.34 omnis dolor repellendus!

因为那是包含“mark”标签的句子,小数点不是句号。

示例2(任何不包含标签但介于其他标签句子之间的句子也将包括在内。)

At vero eos et accusamus et iusto odio dignissimos ducimus. Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do &lt;mark&gt;eiusmod&lt;/mark&gt; tempor incididunt ut labore et dolore &lt;mark&gt;magna&lt;/mark&gt; aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea &lt;mark&gt;commodo&lt;/mark&gt; consequat? Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur! Excepteur sint &lt;mark&gt;occaecat&lt;/mark&gt; cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum; sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam?

应该返回以下内容(请注意句子“Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur!”即使它没有标签,也被包括在内,因为它位于其他两个匹配的句子之间)。

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do &lt;mark&gt;eiusmod&lt;/mark&gt; tempor incididunt ut labore et dolore &lt;mark&gt;magna&lt;/mark&gt; aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea &lt;mark&gt;commodo&lt;/mark&gt; consequat? Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur! Excepteur sint &lt;mark&gt;occaecat&lt;/mark&gt; cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum

【问题讨论】:

    标签: php regex pcre


    【解决方案1】:

    您可以使用符合您要求的 PCRE 正则表达式:

    ((?<!\S)[^.?!;]*?<mark>.+?(?>[.?;!](?!\S)|\z))(?>(?>\h+.+?[.;?!])*?\h+(?1))*
    

    RegEx Demo

    正则表达式详细信息:

    • (?&lt;!\S) 断言我们在当前位置之前没有空格
    • [^.?!;]*?:匹配 0 个或多个未在 [...] 中列出的任何字符
    • (?:\h+.+?[.?!;])*:在标记的句子之间匹配0个或多个句子
    • (?&gt;[.?;!](?!\S)|\z): 断言我们在匹配句子终止符或匹配输入结尾后的当前位置后没有空格
    • (?1) 递归第一个子模式

    【讨论】:

    • 最后一个问题,请。如果文本块有一个最终不完整的句子(即没有终止),但至少包含一个 mark 标签,有没有办法捕获其余可用句子?例如。 velit esse cillum dolore eu fugiat nulla pariatur! Excepteur sint &lt;mark&gt;occaecat&lt;/mark&gt; cupidatat non proident, sunt in 应该从 Exceptioneur 匹配到结尾,即使它是一个不完整的句子。如果从您的解决方案中获得那里不是太复杂,您能否给我一些指示,以便我可以解决您的答案?再次感谢。
    • 我可能可以调整它,但它不会是一个小的变化,因为我们正在递归整个子模式。因为这里是凌晨 3 点 42 分,所以我已经关闭了我的电脑过夜。如果你不介意的话,我会在明天早上我的时间尝试一下
    • 你可以试试这个:regex101.com/r/hP6gS1/521
    • 效果很好!非常感谢!
    • 我不知道 RegEx 中的递归概念。在了解了这个答案后,我在regular-expressions.info/recurse.html 进行了更多探索。
    【解决方案2】:

    其他策略:由于同一段落中两个带有&lt;mark&gt; 的句子之间的句子必须包含在结果中,因此您可以贪婪地匹配开始标记和不包含换行符的结束标记之间的所有内容 (即在同一段落中)。

    ~
    (?<!\S)
    (?> [^.?!;<]* (?:\.(?=\S))? )+
    <mark> .* </mark>
    .*? [.?;!]
    (?!\S)
    ~ix
    

    demo

    或者同样优化:

    ~
    (?<!\S)
    (?> [^.?!;<]* (?:\.(?=\S))? )+
    (*SKIP)
    <mark> (?> [^\n<]* < )+ /mark>
    (?> [^.?;!]* [.?;!] )+?
    (?!\S)
    ~ix
    

    demo

    注意:这个问题也可以不用正则表达式来解决,使用intlBreakIterator

    【讨论】:

    • 感谢您的回答!我希望它对其他人有用:我已经接受了另一个并且也对此表示赞同。对我来说不幸的是,文本源中没有明确定义新的行/段落。
    • @Cogicero:对于段落末尾没有标点符号的结尾句子,您可以将模式更改为:regex101.com/r/4nRop7/4
    • @Cogicero:如果段落“没有明确定义”,您希望如何在同一段落中找到两个带有&lt;mark&gt;的句子之间的句子?
    • 所谓的“段落没有明确定义”,我的意思是文本中没有换行符,没有CRLF。它只是一段长度未知的文本,句子是使用我提到的标点符号定义的。为了在“标记”句子之间找到句子,我希望(1)“匹配”被“标记”的句子,然后(2)在匹配中包含它们之间的任何内容。这意味着匹配现在包含“段落”的一部分(一组句子,带有一些标记)。接受的答案正是这样做的。再次感谢您提交此内容!我希望它对某人有用。
    猜你喜欢
    • 2021-10-18
    • 1970-01-01
    • 2013-07-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-01
    • 1970-01-01
    相关资源
    最近更新 更多