在使用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>
之前和之后的换行都需要从输出流中删除。
如何?
答案 0 :(得分:3)
这是一个经过测试的工作实现,包括如何从文本节点的右侧或左侧修剪空白:
<?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"
,那么{{1}构成“周围文本”,意思是它作为忽略空格的边界。
<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>
此输出是正确的AFAICT。如果没有,请告诉我原因,我会看到修复它。
答案 1 :(得分:1)
尝试如下选择器:
text()[matches(., '\S?\s*$') and not following::text()[matches('\S')] and following::lb[@break="no"]]
这当然是丑陋而低效的。但是可能有用。不起作用,因为正如已经指出的那样,你没有匹配()。我还有另一个去:
好的,我们正在寻找四种不同的场景:
第一个前置非空文本元素,如果它以空格结尾:
lb [@ break ='no'] / preceding :: text()[normalize-space()!=''和string-length(substring-after(。,normalize-space()))!= 0 ] [1]
在前面的第一个非空文本元素后面的空文本元素:
lb [@ break ='no'] / preceding :: text()[normalize-space()=''and preceding :: text()[normalize-space()!='']]
在第一个非空文本元素之前的空文本元素:
lb [@ break ='no'] / following :: text()[normalize-space()!=''和string-length(substring-before(。,normalize-space()))!= 0 ] [1]
首先跟随非空文本元素,如果它在空格中开始:
lb [@ break ='no'] / following :: text()[normalize-space()=''and following :: text()[normalize-space()!='']]
因为您无法在xpath 1.0中使用union,所以您必须使用此方法从上述每个匹配项中调用模板。