【问题标题】:How to extract unique characters from a string using XSLT 1.0?如何使用 XSLT 1.0 从字符串中提取唯一字符?
【发布时间】:2010-02-15 06:50:51
【问题描述】:

我在 XSLT 设计中遇到过的最艰巨的挑战之一 ..

如何复制给定字符串中的唯一字符 ..
测试 xml 是:

<root>
<string>aaeerstrst11232434</string>
</root>

我期望的输出是:

<string>aerst1234</string>

【问题讨论】:

  • 不仅有 XSLT 递归,而且是真正的函数式编程语言(甚至是 XSLT 1.0)。阅读有关 FXSL 的信息——您会发现它有趣、有用且功能最强大——即使是 XSLT 中最具挑战性的问题也能轻松解决。

标签: xml xslt xslt-1.0 fxsl


【解决方案1】:

使用以下 XPath 单行代码

codepoints-to-string(distinct-values(string-to-codepoints(.)))

使用 this 的完整转换如下

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>       
    <xsl:output method="text"/>

    <xsl:template match="string">
      <xsl:value-of select=
      "codepoints-to-string(distinct-values(string-to-codepoints(.)))
      "/>
    </xsl:template>
</xsl:stylesheet>

当此转换应用于最初提供的 XML 文档时

<root>
    <string>aaeerstrst11232434</string>
</root>

产生想要的结果

aerst1234

如果需要 XSLT 1.0 解决方案,请注明,我会提供。

【讨论】:

  • 按承诺发布。不仅有 XSLT 递归,而且它是一种真正的函数式编程语言(甚至是 XSLT 1.0)。了解 FXSL。
【解决方案2】:

这是一个 XSLT 1.0 解决方案:

<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0">

  <xsl:strip-space elements="*"/>
  <xsl:output method="text"/>

  <xsl:template match="string">
    <xsl:call-template name="unique">
      <xsl:with-param name="input" select="."/>
    </xsl:call-template>
  </xsl:template>

  <xsl:template name="unique">
    <xsl:param name="input"/>
    <xsl:param name="output" select="''"/>
    <xsl:variable name="c" select="substring($input, 1, 1)"/>
    <xsl:choose>
      <xsl:when test="not($input)">
        <xsl:value-of select="$output"/>
      </xsl:when>
      <xsl:when test="contains($output, $c)">
        <xsl:call-template name="unique">
          <xsl:with-param name="input" select="substring($input, 2)"/>
          <xsl:with-param name="output" select="$output"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:call-template name="unique">
          <xsl:with-param name="input" select="substring($input, 2)"/>
          <xsl:with-param name="output" select="concat($output, $c)"/>
        </xsl:call-template>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

【讨论】:

  • 我的假设是错误的 .. 我们不能像函数一样使用模板 .. 但你已经证明它错了 .. 我正在学习(还不算太晚)一种递归调用模板。 . 很高兴.. :-)
【解决方案3】:

这是一个 XSLT 1.0 解决方案,比当前选择的答案更短,并且更容易编写,因为它使用了 FXSL 的 str-foldl 模板

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:f="http://fxsl.sf.net/"
 exclude-result-prefixes="f">

 <xsl:import href="str-foldl.xsl"/>
 <xsl:output method="text"/>

 <f:addUnique/>

 <xsl:variable name="vFunAddunique" select=
  "document('')/*/f:addUnique[1]
  "/>

    <xsl:template match="string">
      <xsl:call-template name="str-foldl">
        <xsl:with-param name="pFunc" select="$vFunAddunique"/>
        <xsl:with-param name="pA0" select="''"/>
        <xsl:with-param name="pStr" select="."/>
      </xsl:call-template>
    </xsl:template>

    <xsl:template match="f:addUnique" mode="f:FXSL">
      <xsl:param name="arg1"/>
      <xsl:param name="arg2"/>

      <xsl:value-of select="$arg1"/>
      <xsl:if test="not(contains($arg1, $arg2))">
       <xsl:value-of select="$arg2"/>
      </xsl:if>
    </xsl:template>
</xsl:stylesheet>

将上述转换应用于最初提供的源 XML 文档时

<root>
    <string>aaeerstrst11232434</string>
</root>

产生想要的结果

aerst1234

详细了解 FXSL 1.x(针对 XSLT 1.0)here,以及关于 FXSL 2.x(针对 XSLT 2.0)here

【讨论】:

  • 默认情况下,XslCompiledTransform 类禁用对 XSLT document() 函数和嵌入式脚本的支持。可以通过创建启用了这些功能的 XsltSettings 对象并将其传递给 Load 方法来启用这些功能。我不得不修改 .Net 代码以使用 document() 函数。
  • 很好,如果安全很重要(并且它应该几乎总是很重要),在这种情况下提供您的 oun XmlResolver,这样它就不允许使用任意 URL,例如包含敏感数据的 xml 文件的绝对文件路径。
【解决方案4】:

当我尝试使用更复杂的 XML 时,我遇到了很多 Martin Honnen 的解决方案的问题,它不适用于下面提到的 XML,所以我参考 Dimitre 的 this answer 准备了自己的解决方案 我也可以称之为更有效的解决方案:

这是一个输入 xml:

    <root>
      <string>aabcdbcd1abcdefghijklmanopqrstuvwxyzabcdefgh0123456789ijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz12312489796453134049446798421230156489413210315487804210313264046040489789789745648974321231564648971232344</string>
      <string2>oejrinsjfojofjweofj24798273492jfakjflsdjljk</string2>
    </root>

这里是工作 XSLT 代码:

  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>
  <xsl:template match="text()">
    <xsl:call-template name="unique_chars">
      <xsl:with-param name="input" select="."/>
    </xsl:call-template>
  </xsl:template>

  <xsl:template name="unique_chars">
    <xsl:param name="input"/>
    <xsl:variable name="c">
      <xsl:value-of select="substring($input, 1, 1)"/>
    </xsl:variable>
    <xsl:choose>
      <xsl:when test="not($input)"/>
      <xsl:otherwise>
        <xsl:choose>
          <xsl:when test="contains(substring($input, 2), $c)">
            <xsl:call-template name="unique_chars">
              <xsl:with-param name="input" select="substring($input, 2)"/>
            </xsl:call-template>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="$c"/>
            <xsl:call-template name="unique_chars">
              <xsl:with-param name="input" select="substring($input, 2)"/>
            </xsl:call-template>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

【讨论】:

  • 几个问题: 1. 您不只是简单地使用我发布的解决方案的原因是什么? 2. 为什么要对空格(' ')进行特殊处理?并声明:当已知唯一字符的数量明显小于字符串中的字符总数时,存在比本页显示的算法更有效的算法。我把寻找和实现这个算法作为练习留给你:)
  • (1)我使用.Net代码触发转换,它不允许document()函数,:-|(2)不是专门处理空格字符的目标,我只是将它用于测试目的并在不知不觉中发布了相同的[编辑以避免混淆](3)我很乐意做功课,我会尽力提供更高效的代码:-)
  • 嗯...我在哪里使用document() 函数?除此之外,我多年来一直在使用 .NET XslCompiledTransform 和 XslTransform 并且从未遇到过 document() 函数的任何问题——除非设置和/或 URI-Resolver 明确禁止它。
  • @dimitre,更改 .Net 代码后工作正常。接受了你的解决方案。感谢指出缺陷。 :-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-10
  • 1970-01-01
  • 2013-06-05
  • 1970-01-01
  • 2016-11-05
相关资源
最近更新 更多