【问题标题】:Cut after the closest word boundary after specific length在特定长度后最近的单词边界后剪切
【发布时间】:2017-12-07 17:18:34
【问题描述】:

输入

<items>
    <item>Lorem ipsum dolor sit amet, sed diam nonumy tempor magna aliquyam erat. Ssed diam voluptua.</item>
    <item>Lorem ipsum dolor sit amet, sed diam nonumy<br/>tempor magna aliquyam erat. Ssed diam voluptua.</item>
    <item>Lorem ipsum dolor sit amet, sed diam nonumy <b>tempor magna aliquyam erat</b>. Ssed diam voluptua.</item>
    <item>Lorem ipsum dolor sit amet, sed diam nonumy <b>tempor <i>magna</i> aliquyam erat</b>. Ssed diam voluptua.</item>
    <item>Lorem ipsum dolor sit amet.</item>
</items>

要求

  • “剪切”特定长度后最接近的单词,例如25 个字符(包括空格)
  • 所有nodes() 之前该位置都保持不变
  • 所有* 该位置后获得属性foo="bar"
  • 所有text() 该位置之后都由template with mode='xy' 处理(此模板将丑化文本);该模板将所有相邻的text()-nodes 包装在&lt;o foo="bar"&gt; + 中,并将其本身作为'#'
  • 如果text()-nodes 在部分可读/丑化* 中,这些text()-nodes 被包裹在一个新元素&lt;o foo="bar"&gt; [参见 item[4] => "aliquyam erat"] + 本身作为'#'
  • IF string-length(text()) le $visibleChars THEN 没有任何反应

输出

<!-- middle of word "temppor" -->
<xsl:variable name="visibleChars" select="number(48)"/>
<items>
    <item>Lorem ipsum dolor sit amet, sed diam nonumy tempor<o foo="bar"> #</o></item>
    <item>Lorem ipsum dolor sit amet, sed diam nonumy<br/>tempor<o foo="bar"> #</o></item>
    <item>Lorem ipsum dolor sit amet, sed diam nonumy <b>tempor<o foo="bar">#</o></b><o foo="bar">#</o></item>
    <item>Lorem ipsum dolor sit amet, sed diam nonumy <b>tempor <i foo="bar">#</i><o foo="bar"> #</o></b><o foo="bar">#</o></item>
    <item>Lorem ipsum dolor sit amet.</item>
</items>

伪 xslt 失败

<xsl:templates match="item">
  WHEN count(text()) eq count(node())
  THEN
     Create a $temp of <item> AND insert a <cut/> at position ($visibleChars + CharsTillNextDelimiter)
     <!-- chars to stay visible -->
     <xsl:apply-templates select="$temp/text()[following-sibling::cut]"/>
     <!-- chars to uglify -->
     <xsl:apply-templates select="$temp/text()[preceding-sibling::cut]" mode="xy"/>

  WHEN (*)
  THEN
     NoIDEAToTackleMixedContentFields

...
<xsl:template match="text()" mode="xy">

    <xsl:analyze-string select="." regex="[a-zA-Z0-9]">
        <xsl:matching-substring>
            <xsl:text>#</xsl:text>
        </xsl:matching-substring>
        <xsl:non-matching-substring>
            <xsl:value-of select="."/>
        </xsl:non-matching-substring>
    </xsl:analyze-string>
</xsl:template>
...

我没有正在运行的缩小版 xslt,但有人可能会向我展示满足要求的方法。感谢您提供任何帮助,我希望我在示例中涵盖了所有可能的情况。

【问题讨论】:

    标签: xml xslt xslt-1.0 xslt-2.0


    【解决方案1】:

    我已成功插入cut 元素,然后按如下方式处理修改后的item

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:functx="http://www.functx.com"
        exclude-result-prefixes="xs functx" version="2.0">
    
        <xsl:param name="length" select="48"/>
    
        <xsl:function name="functx:escape-for-regex" as="xs:string">
            <xsl:param name="arg" as="xs:string?"/>
    
            <xsl:sequence
                select="
                    replace($arg,
                    '(\.|\[|\]|\\|\||\-|\^|\$|\?|\*|\+|\{|\}|\(|\))', '\\$1')
                    "/>
    
        </xsl:function>
    
        <xsl:template match="@* | node()" mode="#all">
            <xsl:copy>
                <xsl:apply-templates select="@* | node()" mode="#current"/>
            </xsl:copy>
        </xsl:template>
    
        <xsl:template match="item[string-length() gt $length]">
            <xsl:variable name="head" as="xs:string" select="substring(., 1, $length)"/>
            <xsl:variable name="head-pattern" as="xs:string"
                select="concat('^', functx:escape-for-regex($head), '\S*')"/>
            <xsl:variable name="text-to-cut" as="text()"
                select="descendant::text()[matches(string-join((preceding::text()[. >> current()], .), ''), $head-pattern)][1]"/>
            <xsl:variable name="splitted-text" as="xs:string*">
                <xsl:analyze-string select="$text-to-cut"
                    regex="{if ($text-to-cut is (current()//text())[1]) then $head-pattern else '^\S+'}">
                    <xsl:matching-substring>
                        <xsl:sequence select="."/>
                    </xsl:matching-substring>
                    <xsl:non-matching-substring>
                        <xsl:sequence select="."/>
                    </xsl:non-matching-substring>
                </xsl:analyze-string>
            </xsl:variable>
            <xsl:variable name="modified-copy" as="element(item)">
                <xsl:copy>
                    <xsl:apply-templates mode="modify">
                        <xsl:with-param name="text-to-cut" as="text()" select="$text-to-cut"
                            tunnel="yes"/>
                        <xsl:with-param name="splitted-text" as="xs:string*" select="$splitted-text"
                            tunnel="yes"/>
                    </xsl:apply-templates>
                </xsl:copy>
            </xsl:variable>
            <xsl:message select="'modified-copy', $modified-copy"/>
            <xsl:apply-templates select="$modified-copy" mode="cut"/>
        </xsl:template>
    
        <xsl:template match="item//text()" mode="modify">
            <xsl:param name="text-to-cut" as="text()" tunnel="yes"/>
            <xsl:param name="splitted-text" as="xs:string*" tunnel="yes"/>
            <xsl:choose>
                <xsl:when test=". is $text-to-cut">
                    <xsl:value-of select="$splitted-text[1]"/>
                    <cut/>
                    <xsl:value-of select="$splitted-text[2]"/>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:next-match/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:template>
    
        <xsl:template match="cut" mode="cut"/>
    
        <xsl:template match="*[preceding-sibling::cut]" mode="cut">
            <xsl:copy>
                <xsl:attribute name="foo">bar</xsl:attribute>
                <xsl:apply-templates mode="#current"/>
            </xsl:copy>
        </xsl:template>
    
        <xsl:template match="text()[preceding::cut]" mode="cut">
            <o foo="bar">
                <xsl:analyze-string select="." regex="[a-zA-Z0-9]+">
                    <xsl:matching-substring>
                        <xsl:text>#</xsl:text>
                    </xsl:matching-substring>
                    <xsl:non-matching-substring>
                        <xsl:value-of select="."/>
                    </xsl:non-matching-substring>
                </xsl:analyze-string>
            </o>
        </xsl:template>
    
    </xsl:stylesheet>
    

    我根据您的意见得到的结果与您发布的不完全一样,但很接近,所以我希望您能适应:

    <items>
        <item>Lorem ipsum dolor sit amet, sed diam nonumy tempor<o foo="bar"> # # #. # # #.</o></item>
        <item>Lorem ipsum dolor sit amet, sed diam nonumy<br/>tempor<o foo="bar"> # # #. # # #.</o></item>
        <item>Lorem ipsum dolor sit amet, sed diam nonumy <b>tempor<o foo="bar"> # # #</o></b><o foo="bar">. # # #.</o></item>
        <item>Lorem ipsum dolor sit amet, sed diam nonumy <b>tempor<o foo="bar"> </o><i foo="bar"><o foo="bar">#</o></i><o foo="bar"> # #</o></b><o foo="bar">. # # #.</o></item>
        <item>Lorem ipsum dolor sit amet.</item>
    </items>
    

    【讨论】:

    • 这是一个基于正则表达式的繁重解决方案(因为它是一个基于繁重的字符串操作问题),但我稍后会试一试。感谢您花时间解决问题。
    【解决方案2】:

    我认为最简单的方法是变成只有一个常见特殊字符的字符串,以消除所有其他字符为'.,',请看下面:

    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" >    
    <xsl:output method="xml" version="4.0" encoding="UTF-8" indent="yes" omit-xml-declaration="yes" />    
    <xsl:template match="/">
        <items>
            <xsl:for-each select="items/item">
                <item>
                    <xsl:variable name="main.string" select="."/>
                    <!--transform string with only one delimiter-->
                    <xsl:variable name="format.string" select="translate(normalize-space($main.string), ' ,.', '|')"/>
                    <!--get string till 25th character-->
                    <xsl:variable name="format.limit.string" select="substring(normalize-space($main.string), 0, 24)"/>
                    <!--get 25th character-->
                    <xsl:variable name="format.last.char" select="translate($format.limit.string, substring($format.limit.string, 0, 23), '')"/>      
                    <!--check if the 25th character is delimiter or part of the word-->
                    <xsl:choose>
                        <xsl:when test="$format.last.char = '|'">
                            <xsl:value-of select="substring(normalize-space($format.limit.string), 0, 23)"/>
                            <xsl:element name="o">
                            <xsl:attribute name="foo"><xsl:value-of select="'bar'"/></xsl:attribute>
                            </xsl:element>
                            <xsl:value-of select="substring($main.string, 24, string-length($main.string))"/>
                        </xsl:when>
                        <xsl:otherwise>
                            <!--if cut in the middle of the word then rest part will be added-->
                            <xsl:variable name="final.string" select="concat($format.limit.string, substring-before(substring($format.string, 24, string-length($format.string)), '|'))"/>
                            <xsl:value-of select="$final.string"/>                        
                            <xsl:element name="o">
                            <xsl:attribute name="foo"><xsl:value-of select="'bar'"/></xsl:attribute>
                            </xsl:element>
                            <xsl:value-of select="substring($main.string, string-length($final.string)+1, string-length($main.string))"/>
                        </xsl:otherwise>        
                    </xsl:choose>
                </item>
            </xsl:for-each>
       </items>            
    </xsl:template>
    </xsl:stylesheet>
    

    因此,您将受到 25 个字符的限制(直到单词)- 可以通过我的代码中的 concat 函数添加其他装饰性内容:

    <items>
        <item>Lorem ipsum dolor sit amet<o foo="bar"/>, sed diam nonumy tempor magna aliquyam erat. Ssed diam voluptua.</item>
        <item>Lorem ipsum dolor sit amet<o foo="bar"/>, sed diam nonumytempor magna aliquyam erat. Ssed diam voluptua.</item>
        <item>Lorem ipsum dolor sit amet<o foo="bar"/>, sed diam nonumy tempor magna aliquyam erat. Ssed diam voluptua.</item>
        <item>Lorem ipsum dolor sit amet<o foo="bar"/>, sed diam nonumy tempor magna aliquyam erat. Ssed diam voluptua.</item>
        <item>Lorem ipsum dolor sit amet<o foo="bar"/>.</item>
    </items>
    

    希望它会有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-05-12
      • 2020-12-02
      • 2014-04-13
      • 2021-12-07
      • 2019-07-30
      相关资源
      最近更新 更多