使用XSLT 1.0进行分词和排序

5
我有一个分隔字符串(以下示例中由空格分隔),我需要对其进行标记化、排序,然后重新连接起来,并且我需要使用 XSLT 1.0 来完成所有这些操作。我应该如何做呢?我知道我需要以某种方式使用 xsl:sort,但到目前为止,我尝试过的所有方法都给我带来了一些错误。
例如,如果我运行本帖底部的代码,我会得到这个:
草莓 蓝莓 橙子 覆盆子 青柠 柠檬
如果我想要得到这个结果,我该怎么办呢?
蓝莓 柠檬 青柠 橙子 覆盆子 草莓
请注意,我在使用 XSLT 1.0。
下面是基于 Jeni Tennison 的代码。
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="tokenize1.xsl"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/">
  <xsl:call-template name="tokenize">
    <xsl:with-param name="string" select="'strawberry blueberry orange raspberry lime lemon'" />
  </xsl:call-template>
</xsl:template>

<xsl:template name="tokenize">
  <xsl:param name="string" />
  <xsl:param name="delimiter" select="' '" />
  <xsl:choose>
    <xsl:when test="$delimiter and contains($string, $delimiter)">
      <token>
        <xsl:value-of select="substring-before($string, $delimiter)" />
      </token>
      <xsl:text> </xsl:text>
      <xsl:call-template name="tokenize">
        <xsl:with-param name="string" 
                        select="substring-after($string, $delimiter)" />
        <xsl:with-param name="delimiter" select="$delimiter" />
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <token><xsl:value-of select="$string" /></token>
      <xsl:text> </xsl:text>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

</xsl:stylesheet>
2个回答

4

这是一个效率低下的纯版本1解决方案:

<!-- Sort the tokens -->
<xsl:template name="sortTokens">
  <xsl:param name="tokens" select="''"/>      <!-- The list of tokens -->
  <xsl:param name="separator" select="' '"/>  <!-- What character separates the tokens? -->
  <xsl:param name="pivot" select="''"/>       <!-- A pivot word used to divide the list -->
  <xsl:param name="lessThan" select="''"/>    <!-- Accumulator for tokens less than the pivot (with leading separator) -->
  <xsl:param name="moreThan" select="''"/>    <!-- Accumulator for tokens more than the pivot (with leading separator) -->
  <xsl:param name="leadWith" select="''"/>    <!-- If set, output this before sorting -->
  <xsl:param name="trailWith" select="''"/>   <!-- If set, output this after sorting -->

  <!-- The first token -->
  <xsl:variable name="firstToken" select="substring-before(concat($tokens,$separator),$separator)"/>

  <!-- Is the first token more or less than the pivot? -->
  <xsl:variable name="pivotVsFirstToken">
    <xsl:call-template name="compareStrings">
      <xsl:with-param name="a" select="$pivot"/>
      <xsl:with-param name="b" select="$firstToken"/>
    </xsl:call-template>
  </xsl:variable>

  <xsl:choose>
    <!-- No input, no output -->
    <xsl:when test="$tokens = '' and $pivot = ''"></xsl:when>

    <!-- At the outset, the first token becomes the pivot -->
    <xsl:when test="$pivot = ''">
      <xsl:value-of select="$leadWith"/>
      <xsl:call-template name="sortTokens">
        <xsl:with-param name="separator" select="$separator"/>
        <xsl:with-param name="tokens" select="substring-after($tokens,$separator)"/>
        <xsl:with-param name="pivot" select="$firstToken"/>
      </xsl:call-template>
      <xsl:value-of select="$trailWith"/>
    </xsl:when>

    <!-- When all tokens are in a bucket, output the pivot between sorted buckets -->
    <xsl:when test="$tokens = ''">
      <xsl:call-template name="sortTokens">
        <xsl:with-param name="separator" select="$separator"/>
        <xsl:with-param name="tokens" select="substring-after($lessThan,$separator)"/>
        <xsl:with-param name="trailWith" select="$separator"/>
      </xsl:call-template>
      <xsl:value-of select="$pivot"/>
      <xsl:call-template name="sortTokens">
        <xsl:with-param name="separator" select="$separator"/>
        <xsl:with-param name="tokens" select="substring-after($moreThan,$separator)"/>
        <xsl:with-param name="leadWith" select="$separator"/>
      </xsl:call-template>
    </xsl:when>

    <!-- If the first token is less than the pivot, put it in the lessThan bucket -->
    <xsl:when test="number($pivotVsFirstToken) = 1">
      <xsl:call-template name="sortTokens">
        <xsl:with-param name="separator" select="$separator"/>
        <xsl:with-param name="tokens" select="substring-after($tokens,$separator)"/>
        <xsl:with-param name="pivot" select="$pivot"/>
        <xsl:with-param name="lessThan" select="concat($separator,$firstToken,$lessThan)"/>
        <xsl:with-param name="moreThan" select="$moreThan"/>
      </xsl:call-template>
    </xsl:when>

    <!-- If the first token is more than the pivot, put it in the moreThan bucket -->
    <xsl:otherwise>
      <xsl:call-template name="sortTokens">
        <xsl:with-param name="separator" select="$separator"/>
        <xsl:with-param name="tokens" select="substring-after($tokens,$separator)"/>
        <xsl:with-param name="pivot" select="$pivot"/>
        <xsl:with-param name="lessThan" select="$lessThan"/>
        <xsl:with-param name="moreThan" select="concat($separator,$firstToken,$moreThan)"/>
      </xsl:call-template>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<!-- Quote an apostrophe -->
<xsl:variable name="apos" select="&quot;'&quot;"/>

<!-- The comparison order of the characters -->
<xsl:variable name="characterOrder" select="concat(' !&quot;#$%&amp;',$apos,'()*+,-./0123456789:;&lt;=&gt;?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~')"/>

<!-- Return -1 if string a is less, 1 if string b is less, or 0 if they are equal -->
<xsl:template name="compareStrings">
  <xsl:param name="a" select="''"/>
  <xsl:param name="b" select="''"/>
  <xsl:choose>
    <xsl:when test="$a = '' and $b = ''">0</xsl:when>
    <xsl:when test="$a = ''">-1</xsl:when>
    <xsl:when test="$b = ''">1</xsl:when>
    <xsl:when test="substring($a,1,1) = substring($b,1,1)">
      <xsl:call-template name="compareStrings">
        <xsl:with-param name="a" select="substring($a,2)"/>
        <xsl:with-param name="b" select="substring($b,2)"/>
      </xsl:call-template>
    </xsl:when>
    <xsl:when test="contains(substring-after($characterOrder,substring($a,1,1)),substring($b,1,1))">-1</xsl:when>
    <xsl:otherwise>1</xsl:otherwise>
  </xsl:choose>
</xsl:template>

0

如果您的处理器支持EXSLT,最好使用str:tokenize

排序时,为什么不使用xsl:sort呢?

<xsl:template match="/">
  <xsl:variable name="tokens">
    <xsl:call-template name="tokenize">
      <xsl:with-param name="string" select="'strawberry blueberry orange raspberry lime lemon'" />
    </xsl:call-template>
  </xsl:variable>

  <xsl:for-each select="$tokens">
    <xsl:sort select="text()" />
    <xsl:value-of select="." />
    <xsl:if test="not(last())">
      <xsl:text> </xsl:text>
    </xsl:if>
  </xsl:for-each>
</xsl:template>

请注意,您可能需要使用 exsl:node-set 进行迭代。

假设由于某种原因我不能使用str:tokenize。无论如何,问题出在排序上,而不是分词。 - Paul Reiners
由于xsl:sort适用于节点集,但他的标记仅存在于结果树片段中。 - Ben Blank
1
显然,您应该使用exsl:node-set或者您的XSLT引擎提供的任何东西。RTF在计算中是“最愚蠢的90年代”的官方说法。 - alamar
我确实想使用xsl:sort。此外,如果可以不使用RTF和使用节点集编写代码,那就没问题了。关键是我有一个逗号分隔的字符串需要进行标记化、排序,然后再拼接在一起,而且我需要使用XSLT 1.0来完成所有这些操作。 - Paul Reiners
@Paul Reiners:大多数XSLT处理器都提供了node-set()函数的访问。如果由于某种原因您没有机会使用它,那么您就完全束手无策了。 - Tomalak
显示剩余2条评论

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