在格式化使用TEI标记(www.tei-c.org)的文本文档时会出现此问题。这超出了我的XSLT/XPATH技能。 (需要XSLT/XPATH 1.0中的解决方案。)
有一个标记元素<lb>
,用于标记换行符。它可以采用@break
属性。如果为@break="no"
,则在生成输出时,应忽略<lb>
与周围文本之间的任何空格。
所以
This little tea <lb break="no" />
pot, short and stout.
应该理解为
This little teapot, short and stout.
也就是说,不应在输出流中呈现“tea”之后的空间和“pot”之前的换行符。
对于
<lb>
之前的空格,这可能会起作用:<xsl:template match="text()[following-sibling::*[1][self::lb[@break='no']]">
<!-- Do something about the space here. -->
</xsl:template>
类似的内容对于
<lb>
之后的换行符也适用。好的。但这比较棘手:
This <emph>little <ref>tea </ref> </emph>
<lb break="no" />
pot, short and stout.
现在
<ref>
元素内的文本不是<lb>
的同级对象。并且</ref>
之前的空间,</emph>
之前的空间以及<lb>
之前和之后的换行符都需要从输出流中删除。如何?
最佳答案
这是一个经过测试的有效实现,包括如何从文本节点的右侧或左侧修剪空白:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="node() | @*">
<xsl:copy>
<xsl:apply-templates select="node() | @*"/>
</xsl:copy>
</xsl:template>
<!-- Match if the preceding node (not necessarily sibling) that is either
a non-empty-space-text node or an <lb> is an <lb break='no'> -->
<xsl:template match="text()[
(preceding::node()[
self::text()[normalize-space() != ''] or
self::lb])
[last()]
[self::lb[@break='no']]
]">
<!-- Trim whitespace on the left. Thanks to Alejandro,
http://stackoverflow.com/a/3997107/423105 -->
<xsl:variable name="firstNonSpace"
select="substring(normalize-space(), 1, 1)"/>
<xsl:value-of select="concat($firstNonSpace,
substring-after(., $firstNonSpace))"/>
</xsl:template>
<!-- Match if the next node (not necessarily sibling) that is either
a non-empty-space-text node or an <lb> is an <lb break='no'> -->
<xsl:template match="text()[
following::node()[
self::text()[normalize-space() != ''] or
self::lb]
[1]
[self::lb[@break='no']]
]">
<xsl:variable name="normalized" select="normalize-space()"/>
<xsl:if test="$normalized != ''">
<xsl:variable name="lastNonSpace"
select="substring($normalized, string-length($normalized))"/>
<xsl:variable name="trimmedSuffix">
<xsl:call-template name="substring-after-last">
<xsl:with-param name="string" select="."/>
<xsl:with-param name="delimiter" select="$lastNonSpace"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="substring(., 1, string-length(.) -
string-length($trimmedSuffix))"/>
</xsl:if>
<!-- otherwise output nothing. -->
</xsl:template>
<!-- Thanks to Jeni Tennison:
http://www.stylusstudio.com/xsllist/200111/post00460.html -->
<xsl:template name="substring-after-last">
<xsl:param name="string" />
<xsl:param name="delimiter" />
<xsl:choose>
<xsl:when test="contains($string, $delimiter)">
<xsl:call-template name="substring-after-last">
<xsl:with-param name="string"
select="substring-after($string, $delimiter)" />
<xsl:with-param name="delimiter" select="$delimiter" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise><xsl:value-of select="$string" /></xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
在此之前,我的假设有待上述“下一个歧义”评论的答案,那就是,如果存在一个没有
<lb>
的break="no"
元素,则<lb>
构成了“环绕文本”,因为它是忽略空白的边界。输入样例:
<test>
<t1>
This <emph>little <ref>tea </ref> </emph>
<lb break="no" />
pot, short and stout.
</t1>
<t2>
This <emph>little <ref>tea </ref> </emph>
<lb />
<lb break="no" />
pot, short and stout.
</t2>
</test>
输出:
<test>
<t1>
This <emph>little <ref>tea</ref></emph><lb break="no"/>pot, short and stout.
</t1>
<t2>
This <emph>little <ref>tea </ref> </emph>
<lb/><lb break="no"/>pot, short and stout.
</t2>
</test>
此输出是正确的AFAICT。如果没有,请告诉我原因,我将解决问题。
关于XSLT当元素提示时,如何修剪元素前后的空间?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/10127687/