0

我有大量需要编辑的 HTML(可能还有其他 xml)文档。

编辑通常采用“John Doe”->“[Person A]”的形式。要编辑的文本可能在标题或段落中,但几乎总是在段落中。

真正的简单字符串替换。不是很复杂的事情。

但是,我确实想保留文档结构,并且我不想重新发明任何轮子。文档文本中的字符串替换可能会完成这项工作,但也可能会破坏文档结构,因此它将是最后的选择。

现在我已经盯着 XSLT 看了一个小时,并试图强迫“str:replace”来做我的竞标。我会让您免于查看我无效的微弱尝试,但我会问这个问题:有没有一种简单且已知的方法可以使用 XSLT 应用我的编辑,您可以在这里发布吗?

先感谢您。

更新:应 Martin Honnen 的要求,我正在添加我的输入文件,以及我用来获取最新错误消息的命令。由此可见,当谈到 XSLT 时,我是一个完整的 n00b :-)

.html 文件:

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 过渡//EN">
    <html>
      <头部>
        <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
        <title>今天日期</title>
        <meta name="created" content="2020-11-04T30:45:00"/>
      </head>
      <正文>
        <ol 开始=“2”>
          <li><p> John Doe 和 Henry 一起在 9. fux 2057
          Fluebottom 成立了公司 Doe &; Fluebottom 小部件
          公司。</p>
        </ol>
      </正文>
    </html>

XSLT 转换文件:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        >
<xsl:template match="p">
  <xsl:copy>
<xsl:attribute name="matchesPattern">
  <xsl:copy-of select='str:replace("John Doe", ".*",  "[Person A]")'/>
</xsl:attribute>
  <xsl:copy-of select='str:replace("Henry Fluebottom", ".*",  "[Person B]")'/>
  </xsl:copy>
</xsl:template>
</xsl:stylesheet>

命令和输出:

$  xsltproc -html transform.xsl example.html
xmlXPathCompOpEval: function replace bound to undefined prefix str
xmlXPathCompiledEval: 2 objects left on the stack.
<?xml version="1.0"?>



    TodaysDate




      <p matchesPattern=""/>  

$ 
4

3 回答 3

1

xsltproc 基于 libxslt 并且支持各种 EXSLT 函数str:replace,如要使用它,您需要声明命名空间

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:str="http://exslt.org/strings"
    exclude-result-prefixes="str"
    version="1.0">

    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="p//text()">
        <xsl:value-of select="str:replace(., 'John Doe', '[Person A]')"/>
    </xsl:template>

</xsl:stylesheet>
于 2020-03-23T13:19:29.507 回答
1

XSLT 1.0 中没有简单的方法可以对同一个字符串执行多个替换。您需要使用递归命名模板,一次执行一个替换操作,然后移动到当前查找字符串的下一个实例,或者 - 当不存在下一个实例时 - 到下一个查找/替换对。

考虑以下示例:

输入

<html>
    <head>
        <title>John Doe and Henry Fluebottom</title>
    </head>
    <body>
        <p>John Doe is a person. John Doe on 9. fux 2057 together with Henry Fluebottom formed the company Doe &amp; Fluebottom Widgets Inc. Henry Fluebottom is also a person.</p>
    </body>
</html>

XSLT 1.0(+ EXSLT 节点集()函数)

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" omit-xml-declaration="yes" version="1.0" encoding="utf-8" indent="yes"/>

<!-- identity transform -->
<xsl:template match="@*|node()">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

<xsl:variable name="dictionary">
    <entry find="John Doe" replace="[Person A]"/>
    <entry find="Henry Fluebottom" replace="[Person B]"/>
</xsl:variable>

<xsl:template match="text()">
    <xsl:call-template name="multi-replace">
        <xsl:with-param name="string" select="normalize-space(.)"/>
        <xsl:with-param name="entries" select="exsl:node-set($dictionary)/entry"/>"/>
    </xsl:call-template>
</xsl:template>

<xsl:template name="multi-replace">
    <xsl:param name="string"/>
    <xsl:param name="entries"/>
    <xsl:choose>
        <xsl:when test="$entries">
            <xsl:call-template name="multi-replace">
                <xsl:with-param name="string">
                    <xsl:call-template name="replace">
                        <xsl:with-param name="string" select="$string"/>
                        <xsl:with-param name="search-string" select="$entries[1]/@find"/>
                        <xsl:with-param name="replace-string" select="$entries[1]/@replace"/>
                    </xsl:call-template>
                </xsl:with-param>
                <xsl:with-param name="entries" select="$entries[position() > 1]"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="$string"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

<xsl:template name="replace">
    <xsl:param name="string"/>
    <xsl:param name="search-string"/>
    <xsl:param name="replace-string"/>
    <xsl:choose>
        <xsl:when test="contains($string, $search-string)">
            <xsl:value-of select="substring-before($string, $search-string)"/>
            <xsl:value-of select="$replace-string"/>
            <xsl:call-template name="replace">
                <xsl:with-param name="string" select="substring-after($string, $search-string)"/>
                <xsl:with-param name="search-string" select="$search-string"/>
                <xsl:with-param name="replace-string" select="$replace-string"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="$string"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

</xsl:stylesheet>

结果

<html>
    <head>
        <title>[Person A] and [Person B]</title>
    </head>
    <body>
        <p>[Person A] is a person. [Person A] on 9. fux 2057 together with [Person B] formed the company Doe &amp; Fluebottom Widgets Inc. [Person B] is also a person.</p>
    </body>
</html>

如您所见,这将替换输入文档中任何位置的搜索字符串的所有实例(属性除外),同时保留文档的结构。


请注意,示例中的输入实际上并不包含"Henry Fluebottom"搜索字符串。您可能想通过调用第一个模板来解决这个问题:

<xsl:with-param name="string" select="normalize-space(.)"/>

代替:

<xsl:with-param name="string" select="."/>
于 2020-03-23T14:24:04.867 回答
0

第一个问题是找到一个真正支持字符串替换的 XSLT 处理器。replace() 函数在 XSLT 2.0+ 中是标准的,但在 XSLT 1.0 中不存在。一些 XSLT 1.0 处理器支持在不同名称空间中的扩展函数 str:replace(),但至少,您需要将名称空间声明添加xmlns:str="http://exslt.org/strings"到样式表中才能找到该函数。我不知道这是否可行(我不知道是否有任何方法可以将此函数与 xsltproc 一起使用);我的建议是改用 XSLT 2.0+ 处理器。

下一个问题是调用函数的方式。通常,正确的调用将是

replace(., "John Doe", "[Person A]")

尽管您将不得不再跳几圈才能在同一字符串上进行多次替换。

我不知道你想通过<xsl:attribute name="matchesPattern">指令实现什么。

于 2020-03-23T12:10:15.320 回答