通过 xslt2 将大型文本文件转换为 xml 的性能问题

Performance issue with large text file transformation to xml through xslt2

我正在尝试使用 xslt 2 转换将文本文件转换为 xml 文件。

这是文本文件的内容:

0000000001  0000000001  ED  I   I       1900-01-01  I   VAT000000000000 BE  1   A       CO      S       451     LD      1010    Stanford
0000000002  0000000002  ED  I   I       1900-01-01  I   VAT000000000000 BE  1   A       CO      S       451     LD      1010    Stanford
0000000003  0000000003  ED  I   I       1900-01-01  I   VAT000000000000 BE  1   A       CO      S       451     LD      1010    Stanford

这些列由 "tabulation" 键(制表符)分隔。有些列是空的,但使用制表键将此列与其他列分开。所以我使用了正则表达式来做到这一点(正则表达式不是简化形式,但我认为它可以优化)

这是我的 XSLT 文件:

<?xml version="1.1" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:xd="http://www.oxygenxml.com/ns/doc/xsl"
    xmlns:xsi="setClients.xsd"
    exclude-result-prefixes="xs xd"
    version="2.0">

    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

    <xsl:param name="txt-encoding" as="xs:string" select="'iso-8859-1'"/>
    <xsl:param name="txt-uri" as="xs:string" select="'file:///xxxxxxx.txt'"/>

    <xsl:variable name="txt" select="unparsed-text($txt-uri, $txt-encoding)"/>
    <xsl:variable name="entries" as="node()*">
        <xsl:analyze-string select="$txt" regex="\r\n?|\n">
            <xsl:non-matching-substring>
                <xsl:analyze-string select="." regex="(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)">
                    <xsl:matching-substring>
                        <entry>

                            <!-- * infos client -->
                            <c_id><xsl:value-of select="regex-group(1)"/></c_id>
                            <c_shipo_id><xsl:value-of select="normalize-space(regex-group(2))"/></c_shipo_id>
                            <c_company_id><xsl:value-of select="normalize-space(regex-group(3))"/></c_company_id>
                            <c_type_client><xsl:value-of select="normalize-space(regex-group(4))"/></c_type_client>
                            <c_classe><xsl:value-of select="normalize-space(regex-group(5))"/></c_classe>
                            <c_sous_type_client><xsl:value-of select="normalize-space(regex-group(6))"/></c_sous_type_client>
                            <c_start_date><xsl:value-of select="normalize-space(regex-group(7))"/></c_start_date>
                            <c_type_personne><xsl:value-of select="normalize-space(regex-group(8))"/></c_type_personne>
                            <c_type_personne><xsl:value-of select="normalize-space(regex-group(10))"/></c_type_personne>
                            <c_type_document><xsl:value-of select="normalize-space(regex-group(11))"/></c_type_document>
                            <c_num_tva><xsl:value-of select="normalize-space(regex-group(12))"/></c_num_tva>
                            <c_pays><xsl:value-of select="normalize-space(regex-group(13))"/></c_pays>
                            <c_pays_id><xsl:value-of select="normalize-space(regex-group(14))"/></c_pays_id>
                            <c_raison_sociale><xsl:value-of select="normalize-space(regex-group(15))"/></c_raison_sociale>
                            <c_civilité><xsl:value-of select="normalize-space(regex-group(16))"/></c_civilité>
                            <c_name><xsl:value-of select="normalize-space(regex-group(17))"/></c_name>
                            <c_prenom><xsl:value-of select="normalize-space(regex-group(18))"/></c_prenom>
                            <c_complement><xsl:value-of select="normalize-space(regex-group(19))"/></c_complement>
                            <c_adresse_forcee><xsl:value-of select="normalize-space(regex-group(20))"/></c_adresse_forcee>
                            <c_complement_adr><xsl:value-of select="normalize-space(regex-group(21))"/></c_complement_adr>
                            <c_numero><xsl:value-of select="normalize-space(regex-group(22))"/></c_numero>
                            <c_complement_numero><xsl:value-of select="normalize-space(regex-group(23))"/></c_complement_numero>
                            <c_adresse_rue><xsl:value-of select="normalize-space(regex-group(24))"/></c_adresse_rue>
                            <c_lieu_dit><xsl:value-of select="normalize-space(regex-group(25))"/></c_lieu_dit>
                            <c_code_postal><xsl:value-of select="normalize-space(regex-group(26))"/></c_code_postal>
                            <c_localite><xsl:value-of select="normalize-space(regex-group(27))"/></c_localite>
                            <c_telephone><xsl:value-of select="normalize-space(regex-group(28))"/></c_telephone>
                            <c_telephone_mobile><xsl:value-of select="normalize-space(regex-group(29))"/></c_telephone_mobile>
                            <c_email><xsl:value-of select="normalize-space(regex-group(30))"/></c_email>
                            <c_fax><xsl:value-of select="normalize-space(regex-group(31))"/></c_fax>
                            <c_actif><xsl:value-of select="normalize-space(regex-group(32))"/></c_actif>
                            <c_date_creation><xsl:value-of select="normalize-space(regex-group(33))"/></c_date_creation>
                            <c_langue><xsl:value-of select="normalize-space(regex-group(34))"/></c_langue>
                            <c_sapcode><xsl:value-of select="normalize-space(regex-group(35))"/></c_sapcode>
                            <c_invoicecopies><xsl:value-of select="normalize-space(regex-group(36))"/></c_invoicecopies>
                            <c_flttax><xsl:value-of select="normalize-space(regex-group(37))"/></c_flttax>
                            <c_flttax1><xsl:value-of select="normalize-space(regex-group(38))"/></c_flttax1>
                            <c_flttax2><xsl:value-of select="normalize-space(regex-group(39))"/></c_flttax2>
                            <c_flttax3><xsl:value-of select="normalize-space(regex-group(40))"/></c_flttax3>
                            <c_fldistax><xsl:value-of select="normalize-space(regex-group(41))"/></c_fldistax>
                            <c_equaladressbilling><xsl:value-of select="normalize-space(regex-group(42))"/></c_equaladressbilling>
                            <c_equaladressinvoice><xsl:value-of select="normalize-space(regex-group(43))"/></c_equaladressinvoice>
                            <c_equaladresspayment><xsl:value-of select="normalize-space(regex-group(44))"/></c_equaladresspayment>
                            <c_collectiontype><xsl:value-of select="normalize-space(regex-group(45))"/></c_collectiontype>
                            <c_companycode><xsl:value-of select="normalize-space(regex-group(46))"/></c_companycode>
                            <c_collectionid><xsl:value-of select="normalize-space(regex-group(47))"/></c_collectionid>
                            <c_duedatefree><xsl:value-of select="normalize-space(regex-group(48))"/></c_duedatefree>
                            <c_payements><xsl:value-of select="normalize-space(regex-group(49))"/></c_payements>
                            <c_paynum><xsl:value-of select="normalize-space(regex-group(50))"/></c_paynum>
                            <c_iban><xsl:value-of select="normalize-space(regex-group(51))"/></c_iban>
                            <c_mandate><xsl:value-of select="normalize-space(regex-group(52))"/></c_mandate>
                            <c_mandatedate><xsl:value-of select="normalize-space(regex-group(53))"/></c_mandatedate>
                            <c_expdate><xsl:value-of select="normalize-space(regex-group(54))"/></c_expdate>
                            <c_securitycode><xsl:value-of select="normalize-space(regex-group(55))"/></c_securitycode>
                            <c_authornum><xsl:value-of select="normalize-space(regex-group(56))"/></c_authornum>
                            <c_cardtype><xsl:value-of select="normalize-space(regex-group(57))"/></c_cardtype>
                            <c_bank><xsl:value-of select="normalize-space(regex-group(58))"/></c_bank>
                            <c_fixedue><xsl:value-of select="normalize-space(regex-group(59))"/></c_fixedue>
                            <c_duelastday><xsl:value-of select="normalize-space(regex-group(60))"/></c_duelastday>
                            <c_dateini><xsl:value-of select="normalize-space(regex-group(61))"/></c_dateini>
                            <c_datefin><xsl:value-of select="normalize-space(regex-group(62))"/></c_datefin>
                            <c_compfactid><xsl:value-of select="normalize-space(regex-group(63))"/></c_compfactid>
                            <c_persontype><xsl:value-of select="normalize-space(regex-group(64))"/></c_persontype>

                            <!-- * infos ptv -->

                            <p_id><xsl:value-of select="normalize-space(regex-group(65))"/></p_id>
                            <p_type><xsl:value-of select="normalize-space(regex-group(66))"/></p_type>
                            <p_useguide><xsl:value-of select="normalize-space(regex-group(67))"/></p_useguide>
                            <p_controldelivery><xsl:value-of select="normalize-space(regex-group(68))"/></p_controldelivery>
                            <p_copiesinitonclose><xsl:value-of select="normalize-space(regex-group(69))"/></p_copiesinitonclose>
                            <p_nominative><xsl:value-of select="normalize-space(regex-group(70))"/></p_nominative>
                            <p_sapcode><xsl:value-of select="normalize-space(regex-group(71))"/></p_sapcode>
                            <p_pays><xsl:value-of select="normalize-space(regex-group(72))"/></p_pays>
                            <p_raison_sociale><xsl:value-of select="normalize-space(regex-group(73))"/></p_raison_sociale>
                            <p_civilite><xsl:value-of select="normalize-space(regex-group(74))"/></p_civilite>
                            <p_nom><xsl:value-of select="normalize-space(regex-group(75))"/></p_nom>
                            <p_prenom><xsl:value-of select="normalize-space(regex-group(76))"/></p_prenom>
                            <p_complement><xsl:value-of select="normalize-space(regex-group(77))"/></p_complement>
                            <p_adresse_forcee><xsl:value-of select="normalize-space(regex-group(78))"/></p_adresse_forcee>
                            <p_complement_adr><xsl:value-of select="normalize-space(regex-group(79))"/></p_complement_adr>
                            <p_numero><xsl:value-of select="normalize-space(regex-group(80))"/></p_numero>
                            <p_complement_numero><xsl:value-of select="normalize-space(regex-group(81))"/></p_complement_numero>
                            <p_adresse_rue><xsl:value-of select="normalize-space(regex-group(82))"/></p_adresse_rue>
                            <p_lieu_dit><xsl:value-of select="normalize-space(regex-group(83))"/></p_lieu_dit>
                            <p_code_postal><xsl:value-of select="normalize-space(regex-group(84))"/></p_code_postal>
                            <p_localite><xsl:value-of select="normalize-space(regex-group(85))"/></p_localite>
                            <p_telephone><xsl:value-of select="normalize-space(regex-group(86))"/></p_telephone>
                            <p_telephone_mobile><xsl:value-of select="normalize-space(regex-group(87))"/></p_telephone_mobile>
                            <p_email><xsl:value-of select="normalize-space(regex-group(88))"/></p_email>
                            <p_fax><xsl:value-of select="normalize-space(regex-group(89))"/></p_fax>
                            <p_deliverydays><xsl:value-of select="normalize-space(regex-group(90))"/></p_deliverydays>
                            <p_active><xsl:value-of select="normalize-space(regex-group(91))"/></p_active>
                            <p_gestamp><xsl:value-of select="normalize-space(regex-group(92))"/></p_gestamp>
                            <p_numamp><xsl:value-of select="normalize-space(regex-group(93))"/></p_numamp>
                            <p_numcasieramp><xsl:value-of select="normalize-space(regex-group(94))"/></p_numcasieramp>
                            <p_numboxamp><xsl:value-of select="normalize-space(regex-group(95))"/></p_numboxamp>
                            <p_distributeur><xsl:value-of select="normalize-space(regex-group(96))"/></p_distributeur>
                            <p_enseigneamp><xsl:value-of select="normalize-space(regex-group(97))"/></p_enseigneamp>
                            <p_typeamp><xsl:value-of select="normalize-space(regex-group(98))"/></p_typeamp>
                            <zero><xsl:value-of select="normalize-space(regex-group(99))"/></zero>

                        </entry>

                    </xsl:matching-substring>
                </xsl:analyze-string>
            </xsl:non-matching-substring>
        </xsl:analyze-string>
    </xsl:variable>

    <xsl:template match="/" name="text2xml">

        <clients>
            <xsl:for-each select="$entries">
                <client>
                    <xsl:attribute name="companyID">
                        <xsl:value-of select="c_company_id"/>
                    </xsl:attribute>
                    <xsl:attribute name="clientID" >
                        <xsl:value-of select="c_id"/>
                    </xsl:attribute>
                    <general>
                        <xsl:attribute name="startDate">
                            <xsl:value-of select="c_start_date"/>
                        </xsl:attribute>
                        <xsl:attribute name="country">
                            <xsl:value-of select="c_pays_id"/>
                        </xsl:attribute>
                        <xsl:attribute name="clientType">
                            <xsl:value-of select="c_type_client"/>
                        </xsl:attribute>
                        <xsl:attribute name="clientSubtype">
                            <xsl:value-of select="c_sous_type_client"/>
                        </xsl:attribute>
                        <xsl:attribute name="clientClass">
                            <xsl:value-of select="c_classe"/>
                        </xsl:attribute>
                    </general>
                </client>


            </xsl:for-each>     
        </clients>
    </xsl:template>
</xsl:stylesheet>

因此,当我基于此 xslt 文件(在 oxygen XML EDITOR 上执行)生成 XML 时,当列数 < 20 时,需要一秒钟的时间来完成这些工作。当我添加一些其他列,出现延迟,我应该等待超过 15 秒才能生成 XML 文件。这对我来说是件坏事,因为我需要处理我的 XSLT 文件中的 99 列。

你能告诉我为什么那里有性能问题吗? 我应该怎么办? 任何建议都可以帮助我。

谢谢。

根据我们在评论中的交流,我认为您可以尝试使用 tokenize 而不是 xsl:analyze-string 的替代方法,将输入解析为行,然后将行解析为列,看看是否执行更好。

一个使用标记化的例子是

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs">

   <xsl:variable name="tsv" as="xs:string">0000000001  0000000001  ED  I   I       1900-01-01  I   VAT000000000000 BE  1   A       CO      S       451     LD      1010    Stanford
0000000002  0000000002  ED  I   I       1900-01-01  I   VAT000000000000 BE  1   A       CO      S       451     LD      1010    Stanford
0000000003  0000000003  ED  I   I       1900-01-01  I   VAT000000000000 BE  1   A       CO      S       451     LD      1010    Stanford</xsl:variable>

   <xsl:output indent="yes"/>

   <xsl:variable name="lines" as="xs:string*" select="tokenize($tsv, '\r?\n')[normalize-space()]"/>
   <xsl:variable name="entries" as="node()*">

                    <xsl:for-each select="$lines">
                        <xsl:for-each select="tokenize(., '\s+')"><!-- if the input is really tab separated we need '\t' as the second argument for tokenize -->
                            <entry>
                                <col pos="{position()}">
                                    <xsl:value-of select="normalize-space()"/>
                                </col>
                            </entry>
                        </xsl:for-each>
                    </xsl:for-each>

    </xsl:variable>

    <xsl:template match="/" name="text2xml">

        <clients>
           <xsl:copy-of select="$entries"/>

            <xsl:for-each select="$entries">
                <client>
                    <xsl:attribute name="companyID">
                        <xsl:value-of select="c_company_id"/>
                    </xsl:attribute>
                    <xsl:attribute name="clientID" >
                        <xsl:value-of select="c_id"/>
                    </xsl:attribute>
                    <general>
                        <xsl:attribute name="startDate">
                            <xsl:value-of select="c_start_date"/>
                        </xsl:attribute>
                        <xsl:attribute name="country">
                            <xsl:value-of select="c_pays_id"/>
                        </xsl:attribute>
                        <xsl:attribute name="clientType">
                            <xsl:value-of select="c_type_client"/>
                        </xsl:attribute>
                        <xsl:attribute name="clientSubtype">
                            <xsl:value-of select="c_sous_type_client"/>
                        </xsl:attribute>
                        <xsl:attribute name="clientClass">
                            <xsl:value-of select="c_classe"/>
                        </xsl:attribute>
                    </general>
                </client>


            </xsl:for-each>     
        </clients>
    </xsl:template>
</xsl:transform>

这只会生成相同的列名,要根据您的自定义列名进行调整,请使用

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
  xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs">

   <xsl:variable name="tsv" as="xs:string">0000000001  0000000001  ED  I   I       1900-01-01  I   VAT000000000000 BE  1   A       CO      S       451     LD      1010    Stanford
0000000002  0000000002  ED  I   I       1900-01-01  I   VAT000000000000 BE  1   A       CO      S       451     LD      1010    Stanford
0000000003  0000000003  ED  I   I       1900-01-01  I   VAT000000000000 BE  1   A       CO      S       451     LD      1010    Stanford</xsl:variable>

   <xsl:output indent="yes"/>

   <xsl:variable name="lines" as="xs:string*" select="tokenize($tsv, '\r?\n')[normalize-space()]"/>
   <xsl:variable name="entries" as="node()*">

                    <xsl:for-each select="$lines">

                            <entry>
                            <xsl:variable name="columns" select="tokenize(., '\s+')"/><!-- if the input is really tab separated we need '\t' as the second argument for tokenize -->
                          <c_id><xsl:value-of select="$columns[1]"/></c_id>
                            <c_shipo_id><xsl:value-of select="$columns[2]"/></c_shipo_id>
                            <c_company_id><xsl:value-of select="$columns[3]"/></c_company_id>
                            </entry>

                    </xsl:for-each>

    </xsl:variable>

    <xsl:template match="/" name="text2xml">

        <clients>

            <xsl:for-each select="$entries">
                <client>
                    <xsl:attribute name="companyID">
                        <xsl:value-of select="c_company_id"/>
                    </xsl:attribute>
                    <xsl:attribute name="clientID" >
                        <xsl:value-of select="c_id"/>
                    </xsl:attribute>
                    <general>
                        <xsl:attribute name="startDate">
                            <xsl:value-of select="c_start_date"/>
                        </xsl:attribute>
                        <xsl:attribute name="country">
                            <xsl:value-of select="c_pays_id"/>
                        </xsl:attribute>
                        <xsl:attribute name="clientType">
                            <xsl:value-of select="c_type_client"/>
                        </xsl:attribute>
                        <xsl:attribute name="clientSubtype">
                            <xsl:value-of select="c_sous_type_client"/>
                        </xsl:attribute>
                        <xsl:attribute name="clientClass">
                            <xsl:value-of select="c_classe"/>
                        </xsl:attribute>
                    </general>
                </client>


            </xsl:for-each>     
        </clients>
    </xsl:template>
</xsl:transform>

在线示例位于 http://xsltransform.hikmatu.com/bFukv8f and http://xsltransform.hikmatu.com/bFukv8f/1,用于标记列,其中相邻的制表符表示空列使用 tokenize(., '\t')

我认为性能问题几乎可以肯定在正则表达式中

regex="(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)"

它表现不佳的原因是它具有高度的歧义性,因此回溯。

请记住,.* 将匹配尽可能多的字符 - 包括制表符。所以它会从吞下整行开始,然后意识到这不会导致匹配,然后回溯直到它找到一个制表符,然后意识到这再次失败,等等。最简单的修复方法是将每个 (.*) 替换为 ([^\t]*)(即除制表符之外的任何字符的序列)。但是正如@MartinHonnen 所建议的那样,在选项卡上标记为分隔符要简单得多。