如何在一些非常糟糕的xhtml中替换<>字符

How can I replace <> chars in some really bad xhtml

提问人:ScottD 提问时间:9/29/2023 更新时间:9/30/2023 访问量:69

问:

我正在将 xhtml 文档处理成 TEI。xhtml 文档最初是旧的 .doc 文件。创建文档文件的人使用<>字符来指示何时将字母、单词或单词添加到原始文档中。下面是文档的片段。

<p>
&lt;X&gt; presented &lt;<strike>it</strike>&gt;
to others &lt;X&gt; in the form of a babe, [ERASED] X [END ERASE] &lt;at
first &#8212; and according to their belief her&gt; <strike>her
mental conception and</strike>
         <strike>they called her</strike>
spiritual conception &lt;of man &lt;brought forth&gt; a material man
&lt;and was&gt; <strike>but</strike>
a&gt; <strike>matter, flesh and
bones</strike> &lt;miraculous
conception (<strike>and</strike>&lt;but&gt;
it was n<strike>either</strike>&lt;ot&gt;)
and&gt; and<sup>
            <a shape="rect" class="sdfootnoteanc" name="sdfootnote138anc"
               href="#sdfootnote138sym">
               <sup>138</sup>
            </a>
         </sup>
</p>

我正在使用 xsl 来处理 xhtml 和函数

<xsl:when test="matches($text, '&lt;.+&gt;', 's')">

只要没有任何嵌套对,它就可以工作。下面的文本是上面示例中的嵌套示例。

&lt;of man &lt;brought forth&gt; a material man &lt;and was&gt; <strike>but</strike> a&gt;

matches() 函数失败,因为对于第一个 <,它抓住了第一个 > 它看到的,这是第二个 < 的匹配,而不是第一个。在那之后,事情就走下坡路了。

我正在考虑使用 sed 和正则表达式来替换 &alt > 对,但我不明白这不会因为同样的原因而失败。

寻找有关如何解决此问题的好主意。

谢谢

斯科特

HTML XML XSLT 文档 翻译

评论

2赞 LMC 9/29/2023
你如何判断哪些是添加的,哪些是合法的?请将示例更改为更短、更简单的文本,并明确指出应删除哪些<>
2赞 michael.hor257k 9/29/2023
您的示例显示了结束>与开始<位于不同的文本节点中的情况。我相信这几乎完全排除了正则表达式,即使它是可能的(例如,参见:stackoverflow.com/q/546433/17153010)。或任何类型的字符串操作。但是,当然应该可以(尽管不是微不足道的)遍历单个字符 - 甚至跨同级文本节点 - 同时保持<和>字符的计数,直到找到匹配项。你也没有告诉我们你打算如何处理找到的一对;也许还有另一种方法可以使用。
1赞 Siebe Jongebloed 9/29/2023
也请分享您的预期结果

答:

2赞 Heiko Theißen 9/29/2023 #1

下面说明的方法使用文本替换将角度转换为开始和结束标记。然后,它分析文档并按相反的文档顺序处理元素,以便嵌套到其他插入中的插入在外部插入之前进行处理。此处理包括 上的文本替换,以便可以一起处理元素的所有子元素,无论它们是文本节点还是其他元素。<>ins<ins>innerHTML<ins>

使用哪种文本替换取决于要实现的目标。在此示例中,插入被转换为带有边框的元素,以便可以看到嵌套的效果。<span>

var p = document.querySelector("p");
var doc = new DOMParser().parseFromString(p.innerHTML
  .replace(/&lt;/g, "<ins>")
  .replace(/&gt;/g, "</ins>"), "text/html");
var nodes = [];
for (var node, iter = doc.evaluate("//ins", doc.body, undefined, XPathResult.ORDERED_NODE_ITERATOR_TYPE);
  (node = iter.iterateNext());)
  nodes.unshift(node);
for (node of nodes)
  /* The next statement would destroy any nested <ins> nodes,
     but we have already processed them before. */
  node.outerHTML = '<span>' + node.innerHTML + '</span>';
p.innerHTML = doc.body.innerHTML;
span {
  display: inline-block;
  border: 1px solid black;
  margin: 1px;
}
<p>
  &lt;X&gt; presented &lt;<strike>it</strike>&gt; to others &lt;X&gt; in the form of a babe, [ERASED] X [END ERASE] &lt;at first &#8212; and according to their belief her&gt; <strike>her
mental conception and</strike>
  <strike>they called her</strike> spiritual conception &lt;of man &lt;brought forth&gt; a material man &lt;and was&gt; <strike>but</strike> a&gt; <strike>matter, flesh and
bones</strike> &lt;miraculous conception (<strike>and</strike>&lt;but&gt; it was n<strike>either</strike>&lt;ot&gt;) and&gt; and<sup>
            <a shape="rect" class="sdfootnoteanc" name="sdfootnote138anc"
               href="#sdfootnote138sym">
               <sup>138</sup>
  </a>
  </sup>
</p>

上面的处理是使用 Javascript 的 document.evaluate 方法完成的。

仅具有 XSLT 处理器和文本操作的解决方案可能如下所示:

  1. 在输入文件中,使用 sed 等工具替换 和 with。&lt;<ins>&gt;</ins>
  2. 使用以下 XSL 转换转换输入文件(使用这些替换项)。同样,模板必须根据您想要实现的目标进行调整。match="ins"
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="ins">
    <span style="display: inline-block; border: 1px solid black; margin: 1px;">
      <xsl:apply-templates/>
    </span>
  </xsl:template>
</xsl:stylesheet>

评论

0赞 ScottD 9/30/2023
Heiko,如果我在 javascript 中处理,这是一个很好的解决方案,但正如我上面所说,我使用的是 xsl 脚本或 xslt。我可以从构建文件中调用任何 bash 进程,而不是调用 saxon 处理器。但是我会更舒服地使用 sed 并替换 <使用 <ins> 和 >使用 </INS >就像你的例子一样。
0赞 Heiko Theißen 9/30/2023
请看我的扩充答案。
2赞 Martin Honnen 9/30/2023 #2

请注意,除了 Heiko 的回答之外,如果您使用支持 XSLT 3 的当前版本的 Saxon,则可以从命名模板(作为命令选项)(而不是 source 选项)开始,在读取文件后执行正则表达式字符串操作,然后根据需要使用 or 解析标记,并通过模板推送它, 如需进一步处理:-itxsl:initial-template-sunparsed-textparse-xmlparse-xml-fragment

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="3.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  exclude-result-prefixes="#all">
  
  <xsl:param name="input-html" as="xs:string">sample1.html</xsl:param>
  
  <xsl:param name="regex1" as="xs:string">&amp;lt;</xsl:param>
  <xsl:param name="regex2" as="xs:string">&amp;gt;</xsl:param>
  
  <xsl:param name="element-name" as="xs:string" static="yes" select="'ins'"/>
  
  <xsl:param name="start-tag" as="xs:string" select="'&lt;' || $element-name || '>'"/>
  <xsl:param name="end-tag" as="xs:string" select="'&lt;/' || $element-name || '>'"/>
  
  <xsl:variable name="replaced-html" select="$input-html => unparsed-text() => replace($regex1, $start-tag) => replace($regex2, $end-tag) => parse-xml-fragment()"/>  

  <xsl:mode on-no-match="shallow-copy"/>

  <xsl:template name="xsl:initial-template">
    <xsl:apply-templates select="$replaced-html"/>
  </xsl:template>
  
  <xsl:template _match="{$element-name}">
    <span>
      <xsl:apply-templates/>
    </span>
  </xsl:template>
  
</xsl:stylesheet>