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

发布于 2024-10-31 22:52:18 字数 962 浏览 0 评论 0原文

我有一个函数可以将字符串分割成长度不超过 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>

提前致谢。

I have a function to split a sting into lines that are 76 characters in length or less. The input to this function is binary data, and I suppose due to the length of the binary data I often get a "stack overflow" error. Any way to prevent this, or is there a better way to split a string? This needs to be done using XSL 1.0.

<xsl:template name="splitBinaryData">
    <xsl:param name="txt"/>
    <xsl:param name="width"/>
    <xsl:choose>
        <xsl:when test="string-length($txt) > 76 ">
            <xsl:value-of select="substring($txt, 1, 76)"/><xsl:text>
</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) < 76 or string-length($txt) = 76">
            <xsl:value-of select="$txt"/>
        </xsl:when>
    </xsl:choose>
</xsl:template>

Thanks in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

若沐 2024-11-07 22:52:18

一种可能是将算法转换为尾递归,并希望您的 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) > 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), '
')" name="accum"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:when test="string-length($txt) < 76 or string-length($txt) = 76">
            <xsl:value-of select="concat($accum, $txt)"/>
        </xsl:when>
    </xsl:choose>
</xsl:template>

编辑:另一种选择是应用分而治之类型算法。这将问题分成两个大小大致相等的子问题,然后组合它们的解决方案。所需的堆栈深度大大减少,并且与输入大小呈对数增长,而不是线性增长。这里的技巧是使第一个子字符串的大小为 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 > 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>
</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>

One possibility might be to translate the algorithm to be tail recursive and hope your xslt processor recognizes the pattern and turns it into a loop. Except for saxon, I could not find any information which other xslt processors support tail recursion. The transformation works by introducing an accumulator variable that contains the splitted text. The call-template instruction will then be the last operation the template has to do and can be turned into the equivalent of a goto without consuming any stack.

<xsl:template name="splitBinaryData">
    <xsl:param name="txt"/>
    <xsl:param name="width"/>
    <xsl:param name="accum"/>
    <xsl:choose>
        <xsl:when test="string-length($txt) > 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), '
')" name="accum"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:when test="string-length($txt) < 76 or string-length($txt) = 76">
            <xsl:value-of select="concat($accum, $txt)"/>
        </xsl:when>
    </xsl:choose>
</xsl:template>

Edit: Another option would be to apply a divide and conquer type of algorithm. This splits the problem into two sub-problems of about equal size and then combines their solutions. The required stack depth is greatly reduced and grows logarithmically instead of linearly regarding to the input size. The trick here is to make the first sub-string size be a multiple of 76 characters to avoid inserting additional newlines.

<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 > 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>
</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>
[旋木] 2024-11-07 22:52:18

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

<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, '
')"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

注意:MSXSL 4 在 XHTML 格式 (1.4 MB) 在我的机器上只需 2 秒。

Use "Divide and Conquer" pattern like:

<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, '
')"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

Note: MSXSL 4 splits XSLT 2.0 spec in XHTML format (1.4 MB) in 2 seconds on my machine.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文