【问题标题】:How do I convert HTML percent-encoding to Unicode, with XSLT?如何使用 XSLT 将 HTML 百分比编码转换为 Unicode?
【发布时间】:2012-12-07 18:17:37
【问题描述】:

网上有大量关于此的条目和答案,但它们都与我需要的相反。从我的 iTunes XML 中,我尝试使用 XSLT 样式表将数千个百分比编码的条目以多种语言转换为 Unicode 文本。除了跟踪每个字符并进行替换之外,我还缺少任何功能或过程吗?这是我正在使用的各种示例的一个小示例,第一行是 XML 字符串值,下一行是我尝试生成并输出到文本文件的基本文本。

<string>/iTunes/iTunes%20Music/Droit%20devant/L'odysse%CC%81e.mp3</string>

/iTunes/iTunes Music/Droit devant/L'odyssée.mp3

<string>A%CC%80%20la%20Pe%CC%82che</string>

老生常谈

<string>%D0%97%D0%B0%D0%BF%D0%BE%D0%BC%D0%B8%D0%BD%D0%B0%D0%B8%CC%86</string>

Запоминай

<string>%CE%9A%CE%BF%CC%81%CF%84%CF%83%CC%8C%CE%B1%CF%81%CE%B9</string>

Κότσ̌αρι

对于某些人来说,最后一个可能无法正确显示,因为 hacek/caron 过于醒目。

提前感谢您的任何建议或线索

【问题讨论】:

  • 为什么必须使用 XSLT?不能直接打开文件,解析,替换文本,序列化保存吗?
  • 如果您让我们知道您正在使用哪个版本的 XSLT 以及在哪个平台上会有所帮助?
  • 抱歉,我已经有一段时间没有使用 XML/XSLT,所以我忘记了这些细节。我正在使用 XSLT 2、saxon9he 进行处理,在 Linux 和 OSX 上工作。

标签: html xml xslt unicode percent-encoding


【解决方案1】:

纯 XSLT 2.0 解决方案可以利用 string-to-codepoints()codepoints-to-string() 函数。 utf-8解码有点乱,可以搞定。

这个 XSLT 2.0 样式表...

<xsl:stylesheet version="2.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:so="http://stackoverflow.com/questions/13768754"
  exclude-result-prefixes="xsl xs so">
<xsl:output encoding="UTF-8" omit-xml-declaration="yes" indent="yes" />
<xsl:strip-space elements="*"/>

<xsl:variable name="cp-base" select="string-to-codepoints('0A')" as="xs:integer+" />

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

<xsl:function name="so:utf8decode" as="xs:integer*">
  <xsl:param name="bytes" as="xs:integer*" />
  <xsl:choose>
    <xsl:when test="empty($bytes)" />
    <xsl:when test="$bytes[1] eq 0"><!-- The null character is not valid for XML. -->
      <xsl:sequence select="so:utf8decode( remove( $bytes, 1))" />
    </xsl:when>
    <xsl:when test="$bytes[1] le 127">
      <xsl:sequence select="$bytes[1], so:utf8decode( remove( $bytes, 1))" />
    </xsl:when>
    <xsl:when test="$bytes[1] lt 224">
      <xsl:sequence select="
      ((($bytes[1] - 192) * 64) +
        ($bytes[2] - 128)        ),
        so:utf8decode( remove( remove( $bytes, 1), 1))" />
    </xsl:when>
    <xsl:when test="$bytes[1] lt 240">
      <xsl:sequence select="
      ((($bytes[1] - 224) * 4096) +
       (($bytes[2] - 128) *   64) +
        ($bytes[3] - 128)          ),
        so:utf8decode( remove( remove( remove( $bytes, 1), 1), 1))" />
    </xsl:when>
    <xsl:when test="$bytes[1] lt 248">
      <xsl:sequence select="
      ((($bytes[1] - 240) * 262144) +
       (($bytes[2] - 128) *   4096) +
       (($bytes[3] - 128) *     64) +
        ($bytes[4] - 128)            ),
        so:utf8decode( $bytes[position() gt 4])" />
    </xsl:when>
    <xsl:otherwise>
      <!-- Code-point valid for XML. -->
      <xsl:sequence select="so:utf8decode( remove( $bytes, 1))" />
    </xsl:otherwise>
  </xsl:choose>
</xsl:function>

<xsl:template match="string/text()">
  <xsl:analyze-string select="." regex="(%[0-9A-F]{{2}})+" flags="i">
    <xsl:matching-substring>
      <xsl:variable name="utf8-bytes" as="xs:integer+">
        <xsl:analyze-string select="." regex="%([0-9A-F]{{2}})" flags="i">
          <xsl:matching-substring>
          <xsl:variable name="nibble-pair" select="
            for $nibble-char in string-to-codepoints( upper-case(regex-group(1))) return
              if ($nibble-char ge $cp-base[2]) then
                  $nibble-char - $cp-base[2] + 10
                else
                  $nibble-char - $cp-base[1]" as="xs:integer+" />
            <xsl:sequence select="$nibble-pair[1] * 16 + $nibble-pair[2]" />                
          </xsl:matching-substring>
        </xsl:analyze-string>
      </xsl:variable>
      <xsl:value-of select="codepoints-to-string( so:utf8decode( $utf8-bytes))" />
    </xsl:matching-substring>
    <xsl:non-matching-substring>
      <xsl:value-of select="." />
    </xsl:non-matching-substring>
    <xsl:fallback>
      <!-- For XSLT 1.0 operating in forward compatibility mode,
           just echo -->
      <xsl:value-of select="." />
    </xsl:fallback>
  </xsl:analyze-string>
</xsl:template>

</xsl:stylesheet>

...应用于此输入...

<doc>
    <string>/iTunes/iTunes%20Music/Droit%20devant/L'odysse%CC%81e.mp3</string>
    <string>A%Cc%80%20la%20Pe%CC%82che</string>
    <string>%D0%97%D0%B0%D0%BF%D0%BE%D0%BC%D0%B8%D0%BD%D0%B0%D0%B8%CC%86</string>
    <string>%CE%9A%CE%BF%CC%81%CF%84%CF%83%CC%8C%CE%B1%CF%81%CE%B9</string>
</doc>

..产量..

<doc>
   <string>/iTunes/iTunes Music/Droit devant/L'odyssée.mp3</string>
   <string>À la Pêche</string>
   <string>Запоминай</string>
   <string>Κότσ̌αρι</string>
</doc>

【讨论】:

  • 哇,这比我想象的要复杂。我希望只是缺少一个“decodeURI”功能。对于纯 XSLT 方面,我接受了这个答案。感谢您提供如此参与的答案。
  • 六年多过去了,尽管有明显的需求和许多清晰的用例,但仍然没有原生的“decode-from-uri”功能。非常感谢 Sean 提供的解决方法!
  • 该函数包含一个小错误(($bytes[1] - 224) * 262144)($bytes[1] - 240) * 262144))。感谢 Gerrit Imsieke 的观察。
  • @EiríkrÚtlendi XSLt 是由通常不考虑真实世界用例的学者设计和实现的。它解释了为什么它不是很流行以及它过于迂腐的语法。大多数开发人员宁愿使用他们喜欢的语言和工具来处理 XML;如果 XSLT 更易于使用,它将使 XML 成为内容和数据的默认格式。它可能消除了对内容管理系统的需求。
  • @ATL_DEV:回复:XML 可能是“内容和数据的事实格式”,我不知道我是否相信——XML 本身很简单并且足够简单,但我仍然看到像 JSON 这样的新事物出现,它们基本上再现了几乎相同的数据结构范式。开发者喜欢发明。似乎没有轮子太圆,以至于不时不时地进行一点重新发明。 ?
【解决方案2】:

这是使用java.net.URLDecoder.decode Java 方法的一个选项,但您必须升级到 Saxon-PE(或 EE)或降级到 Saxon-B。

Saxon-B 是免费的,并且仍然是 XSLT 2.0 处理器。两者都可以在这里找到:http://saxon.sourceforge.net/

示例...

XML 输入

<doc>
    <string>/iTunes/iTunes%20Music/Droit%20devant/L'odysse%CC%81e.mp3</string>
    <string>A%CC%80%20la%20Pe%CC%82che</string>
    <string>%D0%97%D0%B0%D0%BF%D0%BE%D0%BC%D0%B8%D0%BD%D0%B0%D0%B8%CC%86</string>
    <string>%CE%9A%CE%BF%CC%81%CF%84%CF%83%CC%8C%CE%B1%CF%81%CE%B9</string>
</doc>

XSLT 2.0(使用 Saxon-PE 9.4 和 Saxon-B 9.1 测试)

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:java-urldecode="java.net.URLDecoder">
    <xsl:output method="xml" encoding="UTF-8" omit-xml-declaration="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:template match="string">
        <xsl:value-of select="java-urldecode:decode(.,'UTF-8')"/>
        <xsl:text>&#xA;</xsl:text>
    </xsl:template>

</xsl:stylesheet>

输出

/iTunes/iTunes Music/Droit devant/L'odyssée.mp3
À la Pêche
Запоминай
Κότσ̌αρι

【讨论】:

  • 非常紧凑的答案。 +1 谢谢!
猜你喜欢
  • 2011-12-29
  • 1970-01-01
  • 2011-11-02
  • 1970-01-01
  • 1970-01-01
  • 2013-11-07
  • 1970-01-01
  • 1970-01-01
  • 2020-01-03
相关资源
最近更新 更多