拆分字符串函数会导致堆栈溢出

时间:2011-04-13 19:05:56

标签: xslt split xslt-1.0

我有一个将sting分成长度为76个字符或更短的行的功能。这个函数的输入是二进制数据,我想由于二进制数据的长度,我经常会遇到“堆栈溢出”错误。有什么方法可以防止这种情况,还是有更好的方法来分割字符串?这需要使用XSL 1.0完成。

<xsl:template name="splitBinaryData">
    <xsl:param name="txt"/>
    <xsl:param name="width"/>
    <xsl:choose>
        <xsl:when test="string-length($txt) &gt; 76 ">
            <xsl:value-of select="substring($txt, 1, 76)"/><xsl:text>&#10;</xsl:text>
            <xsl:call-template name="splitBinaryData">
                <xsl:with-param select="substring($txt, 77)" name="txt"/>
                <xsl:with-param select="$width" name="width"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:when test="string-length($txt) &lt; 76 or string-length($txt) = 76">
            <xsl:value-of select="$txt"/>
        </xsl:when>
    </xsl:choose>
</xsl:template>

提前致谢。

2 个答案:

答案 0 :(得分:1)

一种可能性是将算法转换为tail recursive并希望您的xslt处理器识别该模式并将其转换为循环。除了saxon,我找不到其他xslt处理器支持尾递归的任何信息。转换通过引入包含分割文本的累加器变量来工作。 call-template指令将是模板必须执行的最后一个操作,并且可以转换为goto的等效操作而不消耗任何堆栈。

<xsl:template name="splitBinaryData">
    <xsl:param name="txt"/>
    <xsl:param name="width"/>
    <xsl:param name="accum"/>
    <xsl:choose>
        <xsl:when test="string-length($txt) &gt; 76 ">
            <xsl:call-template name="splitBinaryData">
                <xsl:with-param select="substring($txt, 77)" name="txt"/>
                <xsl:with-param select="$width" name="width"/>
                <xsl:with-param select="concat($accum, substring($txt, 1, 76), '&#10;')" name="accum"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:when test="string-length($txt) &lt; 76 or string-length($txt) = 76">
            <xsl:value-of select="concat($accum, $txt)"/>
        </xsl:when>
    </xsl:choose>
</xsl:template>

编辑:另一种选择是应用divide and conquer类型的算法。这将问题分成两个大小相等的子问题,然后结合他们的解决方案。所需的堆栈深度大大减小并且以对数方式增长而不是线性地关于输入大小。这里的技巧是使第一个子字符串大小为76个字符的倍数,以避免插入额外的换行符。

<xsl:template name="splitBinaryData">
    <xsl:param name="txt"/>
    <xsl:param name="width"/>
    <xsl:variable name="len" select="string-length($txt)" />
    <xsl:choose>
        <xsl:when test="$len &gt; 76 ">
            <!-- process the text in two parts of about the same size,
                 the length of the first part should be a multiple of
                 the needed width -->
            <xsl:variable name="idx" select="ceiling($len div 76 div 2) * 76" />
            <xsl:call-template name="splitBinaryData">
                <xsl:with-param select="substring($txt, 1, $idx)" name="txt"/>
                <xsl:with-param select="$width" name="width"/>
            </xsl:call-template>
            <xsl:text>&#10;</xsl:text>
            <xsl:call-template name="splitBinaryData">
                <xsl:with-param select="substring($txt, $idx+1)" name="txt"/>
                <xsl:with-param select="$width" name="width"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="$txt" />
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

答案 1 :(得分:1)

使用“分而治之”模式,如:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/" name="splitBinaryData">
        <xsl:param name="txt" select="string()"/>
        <xsl:param name="width" select="5"/>
        <xsl:param name="length" select="string-length()"/>
        <xsl:choose>
            <xsl:when test="$length > $width">
                <xsl:variable name="split"
                 select="ceiling($length div $width div 2) * $width"/>
                <xsl:call-template name="splitBinaryData">
                    <xsl:with-param name="txt"
                     select="substring($txt, 1, $split)"/>
                    <xsl:with-param name="width" select="$width"/>
                    <xsl:with-param name="length" select="$split"/>
                </xsl:call-template>
                <xsl:call-template name="splitBinaryData">
                    <xsl:with-param name="txt"
                     select="substring($txt, $split + 1)"/>
                    <xsl:with-param name="width" select="$width"/>
                    <xsl:with-param name="length" select="$length - $split"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="concat($txt, '&#xA;')"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

注意:MSXSL 4在我的计算机上在2秒内在XHTML format(1.4 MB)中拆分XSLT 2.0规范。