【问题标题】:regular expression : ignore html tags [duplicate]正则表达式:忽略html标签[重复]
【发布时间】:2015-10-02 01:23:47
【问题描述】:

我有这样的 HTML 内容:

<p>The bedding was hardly <strong>able to cover</strong> it and seemed ready to slide off any moment.</p>

这是 HTML 的完整版本。 http://collabedit.com/gkuc2

我需要搜索字符串hardly able to cover(只是一个示例),我想忽略我要查找的字符串中的任何 HTML 标记。因为在 HTML 文件中,字符串中有 HTML 标签,简单的搜索是找不到的。

用例是:我有一个文件的两个版本:

  • 带有文本和标签的 HTML 文件
  • 相同的文件,但仅包含原始文本(删除了所有标签和多余的空格)

我要搜索的子字符串(针)来自文本版本(不包含任何 HTML 标签),我想找到它在 HTML 版本(有标签的文件)中的位置。

有效的正则表达式是什么?

【问题讨论】:

  • 你的Complete HTML 在哪里
  • 简单:你不使用正则表达式。正则表达式 + html = 坏主意。使用 DOM 解析器,或剥离所有标签,然后在纯文本上进行正则表达式。
  • 正如@MarcB 所说,这通常不是您使用RegEx 的地方。如果您正在寻找一个简单的查找,您可以尝试将(.*) 放在您要查找的每个单词之间。不过,这可能只有大约 40% 的正确率:即hardly(.*)able(.*)to(.*)cover
  • 我想说的唯一方法是构建某种树。位置树。
  • @MarcB 我已经使用 PHP DOM 扩展对 HTML 文件进行了一些处理(例如获取不带标签的原始文本)。但是现在我需要从纯文本中获取一个子字符串并在 HTML 版本中找到它。

标签: javascript php html regex search


【解决方案1】:

尝试将文本保存在变量或其他内容中,然后删除所有标签并在其中执行正常搜索。 你可以使用一个简单的php函数strip_tags()

编辑: 因此,您可能会尝试查找第一个单词和最后一个单词(或仅查找第一个单词,然后使用其余的结果)来定位字符串,然后解析结果并删除标签并检查它是否是您正在寻找的那个。 就像使用正则表达式: 几乎没有。掩护 甚至 几乎没有。$ 并保存每个结果的位置。 然后在结果上使用 strip_tags() 并分析每个结果是否是您想要的结果。 我知道这是一种奇怪的解决方案,但您可以避免无休止的正则表达式等。

【讨论】:

  • 我已经有了文件的纯文本版本,实际上我需要做的是在 HTML 版本中找到从纯文本版本中获取的子字符串的位置。因此,如果我发现我的子字符串是纯文本版本,它将无济于事,因为我已经知道它在纯文本版本中的位置。
【解决方案2】:

把这个放在每个字母之间:

(?:<[^>]+>)*

并将空格替换为:

(?:\s*<[^>]+>\s*)*\s+(?:\s*<[^>]+>\s*)*

喜欢:

h(?:<[^>]+>)*a(?:<[^>]+>)*r(?:<[^>]+>)*d(?:<[^>]+>)*l(?:<[^>]+>)*y(?:\s*<[^>]+>\s*)*\s+(?:\s*<[^>]+>\s*)*a(?:<[^>]+>)*b(?:<[^>]+>)*l(?:<[^>]+>)*e(?:\s*<[^>]+>\s*)*\s+(?:\s*<[^>]+>\s*)*t(?:<[^>]+>)*o(?:\s*<[^>]+>\s*)*\s+(?:\s*<[^>]+>\s*)*c(?:<[^>]+>)*o(?:<[^>]+>)*v(?:<[^>]+>)*e(?:<[^>]+>)*r

如果你想允许标签分词,你只需要每个字母之间的那些,比如:This is b&lt;b&gt;old&lt;/b&gt;

这是没有字母中断的:

hardly(?:\s*<[^>]+>\s*)*\s+(?:\s*<[^>]+>\s*)*able(?:\s*<[^>]+>\s*)*\s+(?:\s*<[^>]+>\s*)*to(?:\s*<[^>]+>\s*)*\s+(?:\s*<[^>]+>\s*)*cover

这应该适用于大多数情况。但是,如果 Html 格式不正确,其中 未进行 html 编码,您可能会遇到问题。它也可能在脚本块或其他带有 CDATA 部分的元素上中断。

【讨论】:

  • 正则表达式有效,但你能解释一下模式吗?带断字的那个。这对我来说真的很难阅读和理解。
  • 换行符只是寻找一个或多个标签,即&lt;,后跟一个不是&gt;的字符,后跟一个&gt;
  • 我不明白你为什么要寻找一个不是&gt; 的字符,而不是匹配任何字符的.。我用点字符进行了测试,它工作正常。所以我用(?:&lt;.+&gt;)*代替了(?:&lt;[^&gt;]+&gt;)*。为什么第一个比最后一个更好?
  • 所以我简化了表达式:我将(?:&lt;.+&gt;)* 放在字母之间,(?:\s*&lt;.+&gt;\s*)* 放在单词之间,这样就可以了。
  • 因为它在某些情况下不起作用。如果在单词之间使用(?:\s*&lt;.+&gt;\s*)*,则to(?:\s*&lt;.+&gt;\s*)*home 将匹配to&lt;span&gt; never go&lt;/span&gt; home,而(?:&lt;[^&gt;]+&gt;)* 将不匹配。这是因为 &lt;.+&gt; 匹配 &lt;span&gt; never go&lt;/span&gt;