Функция разделения строк вызывает переполнение стека - PullRequest
1 голос
/ 13 апреля 2011

У меня есть функция разбить жало на строки длиной не более 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 ]

1 голос
/ 14 апреля 2011

Используйте шаблон «Разделяй и властвуй», например:

<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 разбивает спецификации XSLT 2.0 в формате XHTML (1,4 МБ) в 2секунд на моей машине.

1 голос
/ 14 апреля 2011

Одной из возможностей может быть перевод алгоритма в хвостовую рекурсию и надеемся, что ваш процессор 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>

Редактировать: Другой вариантбудет применять алгоритм разделяй и властвуй .Это разбивает проблему на две подзадачи примерно одинакового размера, а затем объединяет их решения.Требуемая глубина стека значительно уменьшается и увеличивается логарифмически, а не линейно относительно размера ввода.Хитрость заключается в том, чтобы сделать первый размер подстроки кратным 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>
...