Removing the last characters in an XSLT string

2019-01-12 00:21发布

问题:

In my CMS it is possible to create a new article, and choose an image to be shown on that article. When an image is chosen, a thumbnail of the image will automatically be created as well.

If the uploaded image is called image.jpg, then the corresponding thumbnail will automatically be named image_thumbnail.jpg.

I would now like to use the thumbnail image, everywhere on the website where the article is mentioned, except in the article itself (where the original big image should be shown).

But how can I do that?

I imagine if I could get the original name of the image, and then split it up before the suffix (.jpg, .png, .jpeg etc.) and hardcode _thumbnail after the name, then that would be sufficient.

In other words, I want to take the complete filename, and cut it into two parts, so that I can insert the string _thumbnail between the two parts.

Maybe that would work, but what if an image called image.2horses.jpg (a file with more than one dot in the filename) is uploaded? A naive cut before the '.' wouldn't work here.

Is there a way to get around this? Perhaps by cutting the filename up before the last 4 (.jpg, .png) or 5 (.jpeg) characters?

回答1:

Given the image's filename in $filename,

If you can assume that all images will end in ".jpg" and won't have ".jpg" elsewhere in the filename, then this should work:

<img src="{substring-before($filename, '.jpg')}_thumbnail.jpg" ... />

If you don't know the image type (like, you want to handle gif and png as well), or if you think the extension may occur multiple times in the filename ("image.jpg.jpg"), then you will want a template to help you:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="/">
        <p>
            <xsl:call-template name="image_thumbnail">
            <xsl:with-param name="filename" select="'image.jpg'"/>
            </xsl:call-template>
        </p>

        <p>
            <xsl:call-template name="image_thumbnail">
            <xsl:with-param name="filename" select="'image.09.07.11.jpg'"/>
            </xsl:call-template>
        </p>

        <p>
            <xsl:call-template name="image_thumbnail">
            <xsl:with-param name="filename" select="'image.gif'"/>
            </xsl:call-template>
        </p>

        <p>
            <xsl:call-template name="image_thumbnail">
            <xsl:with-param name="filename" select="'image with spaces.jpg'"/>
            </xsl:call-template>
        </p>

        <p>
            <xsl:call-template name="image_thumbnail">
            <xsl:with-param name="filename" select="'image  with  irregular    spaces.jpg'"/>
            </xsl:call-template>
        </p>

        <p>
            <xsl:call-template name="image_thumbnail">
            <xsl:with-param name="filename" select="'image.jpg.again.jpg'"/>
            </xsl:call-template>
        </p>

    </xsl:template>

    <xsl:template name="image_thumbnail">
    <xsl:param name="filename"/>
        <xsl:choose>
        <xsl:when test="contains($filename, '.')">
            <xsl:variable name="before" select="substring-before($filename, '.')"/>
            <xsl:variable name="after" select="substring-after($filename, '.')"/>
            <xsl:choose>
            <xsl:when test="contains($after, '.')">
                <xsl:variable name="recursive">
                    <xsl:call-template name="image_thumbnail">
                    <xsl:with-param name="filename" select="$after"/>
                    </xsl:call-template>
                </xsl:variable>
                <xsl:value-of select="concat($before, '.', $recursive)"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="concat($before, '_thumbnail.', $after)"/>
            </xsl:otherwise>
            </xsl:choose>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="$filename"/>
        </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

</xsl:stylesheet>


回答2:

Off the top of my head:

<xsl:template name="substring-before-last">
  <xsl:param name="string1" select="''" />
  <xsl:param name="string2" select="''" />

  <xsl:if test="$string1 != '' and $string2 != ''">
    <xsl:variable name="head" select="substring-before($string1, $string2)" />
    <xsl:variable name="tail" select="substring-after($string1, $string2)" />
    <xsl:value-of select="$head" />
    <xsl:if test="contains($tail, $string2)">
      <xsl:value-of select="$string2" />
      <xsl:call-template name="substring-before-last">
        <xsl:with-param name="string1" select="$tail" />
        <xsl:with-param name="string2" select="$string2" />
      </xsl:call-template>
    </xsl:if>
  </xsl:if>
</xsl:template>

Called as:

<xsl:template match="/">

  <xsl:variable name="filename" select="'image.2horses.jpg'" />

  <xsl:variable name="basename">
    <xsl:call-template name="substring-before-last">
      <xsl:with-param name="string1" select="$filename" />
      <xsl:with-param name="string2" select="'.'" />
    </xsl:call-template>
  </xsl:variable>

  <xsl:value-of select="$basename" />

</xsl:template>

Yields:

image.2horses


回答3:

A general solution involving only standard XSLT is somewhat hard since you have to search the string from the end. You can split your filename usings two functions, substring-before-last and substring-after-last. Unfortunately, these functions are not part of XSLT. You can Google and try to find implementations. Assuming you have these two functions implemented as XSLT templates you can then use the following template to generate thumbnail names:

<xsl:template name="thumbnail-name">
  <xsl:param name="file-name"/>
  <xsl:call-template name="substring-before-last">
    <xsl:with-param name="text" select="$file-name"/>
    <xsl:with-param name="chars" select="'.'"/>
  </xsl:call-template>
  <xsl:text>_thumbnail.</xsl:text>
  <xsl:call-template name="substring-after-last">
    <xsl:with-param name="text" select="$file-name"/>
    <xsl:with-param name="chars" select="'.'"/>
  </xsl:call-template>
</xsl:template>

You can use the template like this (assuming the variable $file-name contains the name of the image):

<img>
  <xsl:attribute name="src">
    <xsl:call-template name="thumbnail-name">
      <xsl:with-param name="file-name" select="$file-name"/>
    </xsl:call-template>
  </xsl:attribute>
</img>


回答4:

Have a look at the XPath functions overview at W3Schools, specifically the substring-before method.



回答5:

I believe XPath functions operating on string might help you. I would try with some simple replace or translate.



回答6:

XSLT 2 solution using regexp:

replace($filename, '(\.[^\.]*)$', concat('_thumbnail', '$1'))

Original answer (also XSLT 2): This removes all after the last separator (including the separator). So below the $separatorRegexp could be '\.jpg' or just '\.' and the $separator '.jpg' or '.' in the other case.

string-join(reverse(remove(reverse(tokenize($filename, $separatorRegexp)),1)),$separator)

Eventually the '_thumbnail.jpg' can be appended with concat.



标签: image xslt