在 xslt 1.0 中使用 tokenize and/or analyze-string() 将时间格式 "HH:MM:SS.SSS" 转换为毫秒
Convert time format "HH:MM:SS.SSS" to milliseconds using tokenize and/or analyze-string() in xslt 1.0
我想使用带有 XSLT 1.0 的 tokenize and/or analyze-string() 将像 "HH:MM:SS.SSS" 这样的时间格式转换为毫秒。
以下 xsl 样式表:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:pc="https://purl.org/net/hbuschme/teaching/2019ws-infostruk/podcast/0.3"
xmlns:pt="https://purl.org/net/hbuschme/teaching/2019ws-infostruk/podcast-transcript/0.1"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
exclude-result-prefixes="exsl">
<xsl:output method="xhtml"/>
<xsl:template match="pc:podcast">
<html>
<body>
<h1>Episoden des Podcasts <i><xsl:value-of select="pc:title"/></i></h1>
<xsl:apply-templates select="pc:episode"/>
</body>
</html>
</xsl:template>
<xsl:template match="pc:episode">
<p>
Episode <xsl:value-of select="@episode"/> <xsl:text> </xsl:text> <xsl:number format="1. "/>
<xsl:text></xsl:text>
<b><xsl:value-of select="@title"/></b> <br/>
<xsl:value-of select="pt:transcript"/>
<xsl:for-each select="pc:chapter"><ul>
<xsl:number count="pc:episode|pc:chapter" level="multiple" format="1.1. "/>
<xsl:value-of select="@title" />
</ul></xsl:for-each>
<xsl:for-each select="@url">
<xsl:sort select="@episode" order="descending" /><a href="{@url}"><xsl:apply-templates/></a>
</xsl:for-each>
<xsl:call-template name="time-to-milliseconds">
<xsl:with-param name="time" select="@duration"/>
</xsl:call-template>
<xsl:call-template name="mt">
<xsl:with-param name="time" select="@duration"/>
</xsl:call-template>
</p>
</xsl:template>
<xsl:template name="time-to-milliseconds">
<xsl:param name="time"/>
<xsl:param name="h" select="substring-before($time, ':')"/>
<xsl:param name="m" select="substring-before(substring-after($time,':'),':')"/>
<xsl:param name="s" select="substring-after(substring-after($time,':'),':')"/>
<xsl:choose>
<xsl:when test="contains($h, '00')">
<xsl:value-of select="1000*(60*$m + $s)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="1000*(3600*$h + 60*$m + $s)"/>
</xsl:otherwise>
<xsl:when test="contains($m, '00')">
<xsl:value-of select="1000*(3600*$h + $s)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="1000*(3600*$h + 60*$m + $s)"/>
</xsl:otherwise>
<xsl:when test="contains($s, '00')">
<xsl:value-of select="1000*(3600*$h + 60*$m)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="1000*(3600*$h + 60*$m + $s)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="mt">
<xsl:param name="time"/>
<xsl:param name="h" select="floor($time div 3600000)"/>
<xsl:param name="m" select="floor($time mod 3600000 div 60000)"/>
<xsl:param name="s" select="floor($time mod 60000 div 1000)"/>
<xsl:value-of select="concat($h,':',$m,':',$s,':')"/>
</xsl:template>
</xsl:stylesheet>
预期输出:
00:42 -> 4200
有什么建议吗?我不知道如何或是否可以使用 XSLT 1.0 中的分析字符串功能。能否举例说明如何处理这个问题?
给定 XSLT 3.0,一种方法是将整个表达式包装到 XSLT select 属性中,但要调整单引号与双引号,例如
<xsl:variable name="result" select='
let
$duration := string(/test/@duration),
$components := analyze-string(
$duration,
"(((\d*):)?([0-5]?[0-9]):)?([0-5]?[0-9])(\.([0-9]?[0-9]?[0-9]?))?"
),
$hours := $components//fn:group[@nr="3"]/text(),
$hours := if ($hours != "") then $hours else 0,
$minutes := $components//fn:group[@nr="4"]/text(),
$minutes := if ($minutes != "") then $minutes else 0,
$seconds := $components//fn:group[@nr="5"]/text(),
$seconds := if ($seconds != "") then $seconds else 0,
$millis := $components//fn:group[@nr="7"]/text(),
$millis := if ($millis != "") then $millis else 0
return
xs:int($hours * 60 * 60 * 1000
+ $minutes * 60 * 1000
+ $seconds * 1000
+ $millis)'/>
另一种方法是在更细粒度的级别使用 XSLT 结构重写它:
<xsl:variable name="duration" select="string(/test/@duration)"/>
<xsl:variable name="components" select='analyze-string(
$duration,
"(((\d*):)?([0-5]?[0-9]):)?([0-5]?[0-9])(\.([0-9]?[0-9]?[0-9]?))?"
)'/>
<xsl:variable name="hours" select="$components//fn:group[@nr='3']/text()'/>
等等
第三种方法是将对 fn:analyze-string()
的调用替换为使用 XSLT xsl:analyze-string
指令的内容;然后你可能会有一些在 XSLT 2.0 下工作的东西。
这可能有点乏味,但在纯 XSLT 1.0 中仍然很简单:
<xsl:template name="time-to-milliseconds">
<xsl:param name="time"/>
<xsl:param name="h" select="substring-before($time, ':')"/>
<xsl:param name="m" select="substring-before(substring-after($time,':'),':')"/>
<xsl:param name="s" select="substring-after(substring-after($time,':'),':')"/>
<xsl:value-of select="1000*(3600*$h + 60*$m + $s)"/>
</xsl:template>
演示:https://xsltfiddle.liberty-development.net/6rexjhN
反方向转换,可以使用:
<xsl:template name="milliseconds-to-time">
<xsl:param name="milliseconds"/>
<xsl:variable name="h" select="floor($milliseconds div 3600000)"/>
<xsl:variable name="m" select="floor($milliseconds div 60000) mod 60"/>
<xsl:variable name="s" select="$milliseconds mod 60000 div 1000"/>
<xsl:value-of select="format-number($h, '00')" />
<xsl:value-of select="format-number($m, ':00')" />
<xsl:value-of select="format-number($s, ':00.###')" />
</xsl:template>
我想使用带有 XSLT 1.0 的 tokenize and/or analyze-string() 将像 "HH:MM:SS.SSS" 这样的时间格式转换为毫秒。
以下 xsl 样式表:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:pc="https://purl.org/net/hbuschme/teaching/2019ws-infostruk/podcast/0.3"
xmlns:pt="https://purl.org/net/hbuschme/teaching/2019ws-infostruk/podcast-transcript/0.1"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
exclude-result-prefixes="exsl">
<xsl:output method="xhtml"/>
<xsl:template match="pc:podcast">
<html>
<body>
<h1>Episoden des Podcasts <i><xsl:value-of select="pc:title"/></i></h1>
<xsl:apply-templates select="pc:episode"/>
</body>
</html>
</xsl:template>
<xsl:template match="pc:episode">
<p>
Episode <xsl:value-of select="@episode"/> <xsl:text> </xsl:text> <xsl:number format="1. "/>
<xsl:text></xsl:text>
<b><xsl:value-of select="@title"/></b> <br/>
<xsl:value-of select="pt:transcript"/>
<xsl:for-each select="pc:chapter"><ul>
<xsl:number count="pc:episode|pc:chapter" level="multiple" format="1.1. "/>
<xsl:value-of select="@title" />
</ul></xsl:for-each>
<xsl:for-each select="@url">
<xsl:sort select="@episode" order="descending" /><a href="{@url}"><xsl:apply-templates/></a>
</xsl:for-each>
<xsl:call-template name="time-to-milliseconds">
<xsl:with-param name="time" select="@duration"/>
</xsl:call-template>
<xsl:call-template name="mt">
<xsl:with-param name="time" select="@duration"/>
</xsl:call-template>
</p>
</xsl:template>
<xsl:template name="time-to-milliseconds">
<xsl:param name="time"/>
<xsl:param name="h" select="substring-before($time, ':')"/>
<xsl:param name="m" select="substring-before(substring-after($time,':'),':')"/>
<xsl:param name="s" select="substring-after(substring-after($time,':'),':')"/>
<xsl:choose>
<xsl:when test="contains($h, '00')">
<xsl:value-of select="1000*(60*$m + $s)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="1000*(3600*$h + 60*$m + $s)"/>
</xsl:otherwise>
<xsl:when test="contains($m, '00')">
<xsl:value-of select="1000*(3600*$h + $s)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="1000*(3600*$h + 60*$m + $s)"/>
</xsl:otherwise>
<xsl:when test="contains($s, '00')">
<xsl:value-of select="1000*(3600*$h + 60*$m)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="1000*(3600*$h + 60*$m + $s)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="mt">
<xsl:param name="time"/>
<xsl:param name="h" select="floor($time div 3600000)"/>
<xsl:param name="m" select="floor($time mod 3600000 div 60000)"/>
<xsl:param name="s" select="floor($time mod 60000 div 1000)"/>
<xsl:value-of select="concat($h,':',$m,':',$s,':')"/>
</xsl:template>
</xsl:stylesheet>
预期输出:
00:42 -> 4200
有什么建议吗?我不知道如何或是否可以使用 XSLT 1.0 中的分析字符串功能。能否举例说明如何处理这个问题?
给定 XSLT 3.0,一种方法是将整个表达式包装到 XSLT select 属性中,但要调整单引号与双引号,例如
<xsl:variable name="result" select='
let
$duration := string(/test/@duration),
$components := analyze-string(
$duration,
"(((\d*):)?([0-5]?[0-9]):)?([0-5]?[0-9])(\.([0-9]?[0-9]?[0-9]?))?"
),
$hours := $components//fn:group[@nr="3"]/text(),
$hours := if ($hours != "") then $hours else 0,
$minutes := $components//fn:group[@nr="4"]/text(),
$minutes := if ($minutes != "") then $minutes else 0,
$seconds := $components//fn:group[@nr="5"]/text(),
$seconds := if ($seconds != "") then $seconds else 0,
$millis := $components//fn:group[@nr="7"]/text(),
$millis := if ($millis != "") then $millis else 0
return
xs:int($hours * 60 * 60 * 1000
+ $minutes * 60 * 1000
+ $seconds * 1000
+ $millis)'/>
另一种方法是在更细粒度的级别使用 XSLT 结构重写它:
<xsl:variable name="duration" select="string(/test/@duration)"/>
<xsl:variable name="components" select='analyze-string(
$duration,
"(((\d*):)?([0-5]?[0-9]):)?([0-5]?[0-9])(\.([0-9]?[0-9]?[0-9]?))?"
)'/>
<xsl:variable name="hours" select="$components//fn:group[@nr='3']/text()'/>
等等
第三种方法是将对 fn:analyze-string()
的调用替换为使用 XSLT xsl:analyze-string
指令的内容;然后你可能会有一些在 XSLT 2.0 下工作的东西。
这可能有点乏味,但在纯 XSLT 1.0 中仍然很简单:
<xsl:template name="time-to-milliseconds">
<xsl:param name="time"/>
<xsl:param name="h" select="substring-before($time, ':')"/>
<xsl:param name="m" select="substring-before(substring-after($time,':'),':')"/>
<xsl:param name="s" select="substring-after(substring-after($time,':'),':')"/>
<xsl:value-of select="1000*(3600*$h + 60*$m + $s)"/>
</xsl:template>
演示:https://xsltfiddle.liberty-development.net/6rexjhN
反方向转换,可以使用:
<xsl:template name="milliseconds-to-time">
<xsl:param name="milliseconds"/>
<xsl:variable name="h" select="floor($milliseconds div 3600000)"/>
<xsl:variable name="m" select="floor($milliseconds div 60000) mod 60"/>
<xsl:variable name="s" select="$milliseconds mod 60000 div 1000"/>
<xsl:value-of select="format-number($h, '00')" />
<xsl:value-of select="format-number($m, ':00')" />
<xsl:value-of select="format-number($s, ':00.###')" />
</xsl:template>