根据第一个点 (.) 拆分方程式
Split the equation based on first dot (.)
请建议如何根据第一个点将等式分成两部分。早些时候我得到了 suggestion to Split the Equation 基于来自 michael.hor257k 的 BREAK 评论文本,现在它需要按句点拆分。
XML:
<root>
<body><sec><title>The sec 1</title><p>Text 1</p></sec></body>
<inline-formula>
<math display="inline">
<mi>A</mi>
<mn>4.651</mn>
<mi>The next text</mi>
</math>
</inline-formula>
<inline-formula>
<math display="inline">
<mrow>
<mrow><mi>B</mi></mrow>
<mrow><mn>4.651</mn></mrow>
</mrow>
<mi>The next text</mi>
</math>
</inline-formula>
</root>
XSLT:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="root" select="//inline-formula/*" />
<xsl:template match="/">
<xsl:for-each select="//inline-formula">
<xsl:for-each select="text()">
<xsl:if test="contains(., '.')">
<xsl:apply-templates select="$root">
<xsl:with-param name="i" select="." tunnel="yes"/>
</xsl:apply-templates>
</xsl:if>
</xsl:for-each >
</xsl:for-each>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:param name="i" tunnel="yes"/>
<xsl:if test="descendant-or-self::text()[contains(., '.')]">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
所需结果:
<root>
<body><sec><title>The sec 1</title><p>Text 1</p></sec></body>
<inline-formula>
<math display="inline">
<mi>A</mi>
<mn>4.</mn>
</math>
</inline-formula>
<inline-formula>
<math display="inline">
<!--Text node, before dot is removed -->
<mn>651</mn>
<mi>The next text</mi>
</math>
</inline-formula>
<inline-formula>
<math display="inline">
<mrow>
<mrow><mi>B</mi></mrow>
<mrow><mn>4.</mn></mrow>
</mrow>
</math>
</inline-formula>
<inline-formula>
<math display="inline">
<mrow>
<!--Text node, before dot is removed -->
<mrow><mn>651</mn></mrow>
</mrow>
<mi>The next text</mi>
</math>
</inline-formula>
</root>
我也想知道为什么你需要这样的转换,但这里有一个可能的解决方案。我不清楚规则,例如
inline-formula
中可以有超过 2 个 mn
元素吗?
- 总是
mn
的字符串值需要拆分成单独的元素吗?
- 你是说分裂应该发生在第一个
.
的值mn
,但是多个点在单个中没有意义mn
MathML 中的元素
但抛开所有这些,也许通过两个单独的转换更容易解决问题。第一个简单的分隔mn
个元素的内容:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="mn[contains(.,'.')]">
<xsl:for-each select="tokenize(.,'\.')">
<mn>
<xsl:value-of select="."/>
<xsl:if test="position() = 1">
<xsl:text>.</xsl:text>
</xsl:if>
</mn>
</xsl:for-each>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
中间结果为
<?xml version="1.0" encoding="UTF-8"?>
<root>
<body>
<sec>
<title>The sec 1</title>
<p>Text 1</p>
</sec>
</body>
<inline-formula>
<math display="inline">
<mi>A</mi>
<mn>4.</mn>
<mn>651</mn>
<mi>The next text</mi>
</math>
</inline-formula>
<inline-formula>
<math display="inline">
<mrow>
<mrow>
<mi>B</mi>
</mrow>
<mrow>
<mn>4.</mn>
<mn>651</mn>
</mrow>
</mrow>
<mi>The next text</mi>
</math>
</inline-formula>
</root>
然后,应用类似于以下的第二个转换。顺便说一句,这似乎是使用特殊模式关键字 #all
和 #current
.
的好机会
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="inline-formula[count(//mn) gt 1]">
<xsl:apply-templates select="." mode="first"/>
<xsl:apply-templates select="." mode="second"/>
</xsl:template>
<xsl:template match="mn[position() = 2] | mi[. = 'The next text']" mode="first"/>
<xsl:template match="mi[. != 'The next text']" mode="second"/>
<xsl:template match="mn[position() = 1]" mode="second">
<xsl:comment>Text node, before dot is removed</xsl:comment>
</xsl:template>
<xsl:template match="@*|node()" mode="#all">
<xsl:copy>
<xsl:apply-templates select="@*|node()" mode="#current"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
最后的结果是
<?xml version="1.0" encoding="UTF-8"?>
<root>
<body>
<sec>
<title>The sec 1</title>
<p>Text 1</p>
</sec>
</body>
<inline-formula>
<math display="inline">
<mi>A</mi>
<mn>4.</mn>
</math>
</inline-formula>
<inline-formula>
<math display="inline"><!--Text node, before dot is removed-->
<mn>651</mn>
<mi>The next text</mi>
</math>
</inline-formula>
<inline-formula>
<math display="inline">
<mrow>
<mrow>
<mi>B</mi>
</mrow>
<mrow>
<mn>4.</mn>
</mrow>
</mrow>
</math>
</inline-formula>
<inline-formula>
<math display="inline">
<mrow>
<mrow/>
<mrow><!--Text node, before dot is removed-->
<mn>651</mn>
</mrow>
</mrow>
<mi>The next text</mi>
</math>
</inline-formula>
</root>
结果包含一个空的 mrow
元素。如果重要,您可以按照
添加另一个模板
<xsl:template match="mrow/mrow[not(mn)]" mode="second"/>
到第二个转换,但是,同样,不清楚应该如何处理空元素。
查看由 michael.hor257k 提供的上一个问题的答案,您在这个问题中使用的 XSLT 有几个主要区别。在之前的答案中,它对评论进行了拆分,它迭代了此类评论出现的次数
<xsl:for-each select="0 to count(//comment()[.='Break'])">
因此,在新的解决方案中,您需要迭代一个文本节点出现的次数:
<xsl:for-each select="0 to count(//text()[contains(., '.')])">
然后,在"identity"模板中,上一个答案检查当前节点下方的评论数,看是否显示已复制:
<xsl:if test="descendant-or-self::text()[count(preceding::comment()[.='Break'])=$i]">
这意味着,在新的解决方案中,您可以这样写:
<xsl:if test="descendant-or-self::text()[count(preceding::text()[contains(., '.')])=$i]">
然而,这并不完全正确,因为带点的节点将被复制到拆分的第一部分,但拆分的第二部分根本不包含该节点。
实际需要的表达式是这样的:
<xsl:if test="descendant-or-self::text()[(count(preceding::text()[contains(., '.')])=($i - 1) and contains(., '.')) or count(preceding::text()[contains(., '.')])=$i]">
这会将包含点的节点复制到拆分的两个部分。然后,您将需要一个全新的模板来实际拆分文本。
试试这个 XSLT
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="root" select="/*" />
<xsl:template match="/*">
<xsl:copy>
<xsl:copy-of select="*[not(self::inline-formula)]" />
<xsl:for-each select="0 to count(//text()[contains(., '.')])">
<xsl:apply-templates select="$root/inline-formula">
<xsl:with-param name="i" select="." tunnel="yes"/>
</xsl:apply-templates>
</xsl:for-each >
</xsl:copy>
</xsl:template>
<xsl:template match="@*">
<xsl:copy />
</xsl:template>
<xsl:template match="node()">
<xsl:param name="i" tunnel="yes"/>
<xsl:if test="descendant-or-self::text()[(count(preceding::text()[contains(., '.')])=($i - 1) and contains(., '.')) or count(preceding::text()[contains(., '.')])=$i]">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:if>
</xsl:template>
<xsl:template match="text()[contains(., '.')]">
<xsl:param name="i" tunnel="yes"/>
<xsl:choose>
<xsl:when test="count(preceding::text()[contains(., '.')]) = $i">
<xsl:value-of select="substring-before(., '.')" /><xsl:text>.</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring-after(., '.')" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
请建议如何根据第一个点将等式分成两部分。早些时候我得到了 suggestion to Split the Equation 基于来自 michael.hor257k 的 BREAK 评论文本,现在它需要按句点拆分。
XML:
<root>
<body><sec><title>The sec 1</title><p>Text 1</p></sec></body>
<inline-formula>
<math display="inline">
<mi>A</mi>
<mn>4.651</mn>
<mi>The next text</mi>
</math>
</inline-formula>
<inline-formula>
<math display="inline">
<mrow>
<mrow><mi>B</mi></mrow>
<mrow><mn>4.651</mn></mrow>
</mrow>
<mi>The next text</mi>
</math>
</inline-formula>
</root>
XSLT:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="root" select="//inline-formula/*" />
<xsl:template match="/">
<xsl:for-each select="//inline-formula">
<xsl:for-each select="text()">
<xsl:if test="contains(., '.')">
<xsl:apply-templates select="$root">
<xsl:with-param name="i" select="." tunnel="yes"/>
</xsl:apply-templates>
</xsl:if>
</xsl:for-each >
</xsl:for-each>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:param name="i" tunnel="yes"/>
<xsl:if test="descendant-or-self::text()[contains(., '.')]">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
所需结果:
<root>
<body><sec><title>The sec 1</title><p>Text 1</p></sec></body>
<inline-formula>
<math display="inline">
<mi>A</mi>
<mn>4.</mn>
</math>
</inline-formula>
<inline-formula>
<math display="inline">
<!--Text node, before dot is removed -->
<mn>651</mn>
<mi>The next text</mi>
</math>
</inline-formula>
<inline-formula>
<math display="inline">
<mrow>
<mrow><mi>B</mi></mrow>
<mrow><mn>4.</mn></mrow>
</mrow>
</math>
</inline-formula>
<inline-formula>
<math display="inline">
<mrow>
<!--Text node, before dot is removed -->
<mrow><mn>651</mn></mrow>
</mrow>
<mi>The next text</mi>
</math>
</inline-formula>
</root>
我也想知道为什么你需要这样的转换,但这里有一个可能的解决方案。我不清楚规则,例如
inline-formula
中可以有超过 2 个mn
元素吗?- 总是
mn
的字符串值需要拆分成单独的元素吗? - 你是说分裂应该发生在第一个
.
的值mn
,但是多个点在单个中没有意义mn
MathML 中的元素
但抛开所有这些,也许通过两个单独的转换更容易解决问题。第一个简单的分隔mn
个元素的内容:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="mn[contains(.,'.')]">
<xsl:for-each select="tokenize(.,'\.')">
<mn>
<xsl:value-of select="."/>
<xsl:if test="position() = 1">
<xsl:text>.</xsl:text>
</xsl:if>
</mn>
</xsl:for-each>
</xsl:template>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
中间结果为
<?xml version="1.0" encoding="UTF-8"?>
<root>
<body>
<sec>
<title>The sec 1</title>
<p>Text 1</p>
</sec>
</body>
<inline-formula>
<math display="inline">
<mi>A</mi>
<mn>4.</mn>
<mn>651</mn>
<mi>The next text</mi>
</math>
</inline-formula>
<inline-formula>
<math display="inline">
<mrow>
<mrow>
<mi>B</mi>
</mrow>
<mrow>
<mn>4.</mn>
<mn>651</mn>
</mrow>
</mrow>
<mi>The next text</mi>
</math>
</inline-formula>
</root>
然后,应用类似于以下的第二个转换。顺便说一句,这似乎是使用特殊模式关键字 #all
和 #current
.
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="inline-formula[count(//mn) gt 1]">
<xsl:apply-templates select="." mode="first"/>
<xsl:apply-templates select="." mode="second"/>
</xsl:template>
<xsl:template match="mn[position() = 2] | mi[. = 'The next text']" mode="first"/>
<xsl:template match="mi[. != 'The next text']" mode="second"/>
<xsl:template match="mn[position() = 1]" mode="second">
<xsl:comment>Text node, before dot is removed</xsl:comment>
</xsl:template>
<xsl:template match="@*|node()" mode="#all">
<xsl:copy>
<xsl:apply-templates select="@*|node()" mode="#current"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
最后的结果是
<?xml version="1.0" encoding="UTF-8"?>
<root>
<body>
<sec>
<title>The sec 1</title>
<p>Text 1</p>
</sec>
</body>
<inline-formula>
<math display="inline">
<mi>A</mi>
<mn>4.</mn>
</math>
</inline-formula>
<inline-formula>
<math display="inline"><!--Text node, before dot is removed-->
<mn>651</mn>
<mi>The next text</mi>
</math>
</inline-formula>
<inline-formula>
<math display="inline">
<mrow>
<mrow>
<mi>B</mi>
</mrow>
<mrow>
<mn>4.</mn>
</mrow>
</mrow>
</math>
</inline-formula>
<inline-formula>
<math display="inline">
<mrow>
<mrow/>
<mrow><!--Text node, before dot is removed-->
<mn>651</mn>
</mrow>
</mrow>
<mi>The next text</mi>
</math>
</inline-formula>
</root>
结果包含一个空的 mrow
元素。如果重要,您可以按照
<xsl:template match="mrow/mrow[not(mn)]" mode="second"/>
到第二个转换,但是,同样,不清楚应该如何处理空元素。
查看由 michael.hor257k 提供的上一个问题的答案,您在这个问题中使用的 XSLT 有几个主要区别。在之前的答案中,它对评论进行了拆分,它迭代了此类评论出现的次数
<xsl:for-each select="0 to count(//comment()[.='Break'])">
因此,在新的解决方案中,您需要迭代一个文本节点出现的次数:
<xsl:for-each select="0 to count(//text()[contains(., '.')])">
然后,在"identity"模板中,上一个答案检查当前节点下方的评论数,看是否显示已复制:
<xsl:if test="descendant-or-self::text()[count(preceding::comment()[.='Break'])=$i]">
这意味着,在新的解决方案中,您可以这样写:
<xsl:if test="descendant-or-self::text()[count(preceding::text()[contains(., '.')])=$i]">
然而,这并不完全正确,因为带点的节点将被复制到拆分的第一部分,但拆分的第二部分根本不包含该节点。
实际需要的表达式是这样的:
<xsl:if test="descendant-or-self::text()[(count(preceding::text()[contains(., '.')])=($i - 1) and contains(., '.')) or count(preceding::text()[contains(., '.')])=$i]">
这会将包含点的节点复制到拆分的两个部分。然后,您将需要一个全新的模板来实际拆分文本。
试试这个 XSLT
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="root" select="/*" />
<xsl:template match="/*">
<xsl:copy>
<xsl:copy-of select="*[not(self::inline-formula)]" />
<xsl:for-each select="0 to count(//text()[contains(., '.')])">
<xsl:apply-templates select="$root/inline-formula">
<xsl:with-param name="i" select="." tunnel="yes"/>
</xsl:apply-templates>
</xsl:for-each >
</xsl:copy>
</xsl:template>
<xsl:template match="@*">
<xsl:copy />
</xsl:template>
<xsl:template match="node()">
<xsl:param name="i" tunnel="yes"/>
<xsl:if test="descendant-or-self::text()[(count(preceding::text()[contains(., '.')])=($i - 1) and contains(., '.')) or count(preceding::text()[contains(., '.')])=$i]">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:if>
</xsl:template>
<xsl:template match="text()[contains(., '.')]">
<xsl:param name="i" tunnel="yes"/>
<xsl:choose>
<xsl:when test="count(preceding::text()[contains(., '.')]) = $i">
<xsl:value-of select="substring-before(., '.')" /><xsl:text>.</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="substring-after(., '.')" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>