XSL多次搜索和替换函数

4

我正在尝试使用XSL的translate()函数来创建类似于搜索和替换功能,如下所示:

<xsl:template name="create-id">
    <xsl:param name="id" />
    <xsl:call-template name="search-and-replace">
        <xsl:with-param name="str" select="$id" />
        <xsl:with-param name="search">0123456789</xsl:with-param>
        <xsl:with-param name="replace">abcdefghij</xsl:with-param>
    </xsl:call-template>
</xsl:template>

<xsl:template name="search-and-replace">
    <xsl:param name="str" />
    <xsl:param name="search" />
    <xsl:param name="replace" />
    <xsl:variable name="newstr" select="translate($str, $search,
    $replace)" />
    <xsl:choose>
        <xsl:when test="contains($newstr, $search)">
            <xsl:call-template name="search-and-replace">
                <xsl:with-param name="str" select="$newstr" />
                <xsl:with-param name="search" select="$search" />
                <xsl:with-param name="replace" select="$replace" />
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="$newstr" />
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

然而,我的逻辑有些问题,因为它似乎在返回的字符串中删除了最后一个字符。我的猜测是,translate() 只替换了字符串中每个字符的第一个实例,并不是真正的递归替换。如果您有任何想法或意见,将不胜感激。

1
请提供一些输入和输出的示例,以及您期望看到的内容。Translate() 可能不会像您想象的那样工作,但如果没有示例,我们就无法确定具体情况。 - Jim Garrison
2
translate()并不是你想象中的那样。仅仅通过函数名猜测它的功能是不明智的,最好先阅读规范。 - Michael Kay
好问题,+1。请查看我的答案,其中包含了一个完整的XSLT 1.0解决多重替换问题的方案。同时也提供了详细的解释。 - Dimitre Novatchev
4个回答

9
< p > translate()函数只能将一个单字符替换为另一个单字符(或为空字符(删除))。因此,它无法解决字符串替换问题。

这里是一个完整的XSLT 1.0解决多重替换问题的方案:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:my="my:my">
    <xsl:output omit-xml-declaration="yes"/>
    <xsl:strip-space elements="*"/>

    <my:params xml:space="preserve">
        <pattern>
            <old>&#xA;</old>
            <new><br/></new>
        </pattern>
        <pattern>
            <old>quick</old>
            <new>slow</new>
        </pattern>
        <pattern>
            <old>fox</old>
            <new>elephant</new>
        </pattern>
        <pattern>
            <old>brown</old>
            <new>white</new>
        </pattern>
    </my:params>

    <xsl:variable name="vPats"
         select="document('')/*/my:params/*"/>

    <xsl:template match="text()" name="multiReplace">
        <xsl:param name="pText" select="."/>
        <xsl:param name="pPatterns" select="$vPats"/>

        <xsl:if test="string-length($pText) >0">
            <xsl:variable name="vPat" select=
            "$vPats[starts-with($pText, old)][1]"/>

            <xsl:choose>
                <xsl:when test="not($vPat)">
                    <xsl:copy-of select="substring($pText,1,1)"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:copy-of select="$vPat/new/node()"/>
                </xsl:otherwise>
            </xsl:choose>

            <xsl:call-template name="multiReplace">
                <xsl:with-param name="pText" select=
                "substring($pText, 1 + not($vPat) + string-length($vPat/old/node()))"/>
            </xsl:call-template>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

当应用此转换到以下XML文档时:
<t>The quick
brown fox</t>

期望的正确结果已经生成:

The slow<br />white elephant

说明:

  1. 使用递归调用自身的命名模板。

  2. 所有多重替换模式 --> 替换对都在单个外部参数中提供,此处为方便起见,作为全局级别元素<my:params> 内联指定。

  3. 递归遍历源字符串中的每个字符(从左到右),并找到以该字符在该位置开始的第一个模式。

  4. 替换不仅可以是字符串,还可以是任何节点。在这种特定情况下,我们将每个NL字符替换为<br/>元素。


感谢您的输入和解释。我会看看这个解决方案是否适用于我的情况。 - John
@dimitre-novatchev,看起来这对我很有用。然而,我需要至少为给定的样式表定义两组参数。由于我在使用XSL命名空间方面的经验有限,您如何修改您的代码以将两个不同的参数集传递给multiReplace调用? - John
@John:<my:params>树应该作为参数传递,或者它可以在一个单独的XML文件中,文件名应该作为参数传递给转换。现在,将外部参数传递给转换取决于特定的XSLT处理器--需要阅读其文档。我认为你最好的方法是接受这个答案,然后提出一个额外的问题,指定你正在使用的XSLT处理器,并询问如何为此XSLT处理器传递外部参数。我或其他人都可以给出好的答案。 - Dimitre Novatchev

3

str_replace($search, $replace, $subject)

translate($arg, $mapString, $transString)函数的定义如下:

返回修改后的$arg值,使得$arg值中的每个字符都被替换为$mapString值中相应位置处出现的字符在$transString值中相应位置处的字符。

也就是说,它不是用一个字符串替换另一个子字符串,而是将字符映射到其他字符。如果要进行子字符串替换,请使用类似以下代码:

str_replace($search, $replace, $subject)

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

我实际上正在寻找的是字符串中多个实例字符映射,而不是搜索和替换。如果我的原始帖子不清楚,我很抱歉。 - John

0

看起来你的 $search 变量永远不会被实现,因为你已经在代码中用 $replace 替换了每个字符。

<xsl:variable name="newstr" select="translate($str, $search,
    $replace)" />

事先。


0

谢谢。我不确定我们的系统是否支持exslt,但我会调查一下。 - John
提供的 XSD 应该仍然有效,因为它没有使用任何特殊的东西。 - earlNameless

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