2012-01-31 3 views
7

У меня есть строка в файле XML, который выглядит примерно так:Получение подстроки ПОСЛЕ последнего вхождения символа в XSLT

M: Namespace.Class.Method (что-то, что-то б)

Число символов периода (.) Является абримативным, то есть оно может быть только 2, как в этом примере, но может быть больше.

Я хотел бы использовать XSLT, чтобы получить подстроку этой строки из последнего '.' характер, так что я останется только:

метод (что-то, что-то б)

Я не мог этого добиться, используя стандартную подстроку/подстроку-после того, как функции.

Есть ли простой способ сделать это?

+0

Dupe of http://stackoverflow.com/questions/14527/xslt-reverse-find-in-a-string? –

ответ

19

В XSLT 1.0 вам нужно будет использовать рекурсивный шаблон, как это:

<xsl:template name="substring-after-last"> 
    <xsl:param name="string" /> 
    <xsl:param name="delimiter" /> 
    <xsl:choose> 
     <xsl:when test="contains($string, $delimiter)"> 
     <xsl:call-template name="substring-after-last"> 
      <xsl:with-param name="string" 
      select="substring-after($string, $delimiter)" /> 
      <xsl:with-param name="delimiter" select="$delimiter" /> 
     </xsl:call-template> 
     </xsl:when> 
     <xsl:otherwise><xsl:value-of 
        select="$string" /></xsl:otherwise> 
    </xsl:choose> 
    </xsl:template> 

и вызывать его так:

<xsl:call-template name="substring-after-last"> 
    <xsl:with-param name="string" select="'M:Namespace.Class.Method(Something a, Something b)'" /> 
    <xsl:with-param name="delimiter" select="'.'" /> 
</xsl:call-template> 

В XSLT 2.0, вы можете использовать функцию tokenize() и просто выбрать последний элемент в последовательности:

tokenize('M:Namespace.Class.Method(Something a, Something b)','\.')[last()] 
+0

Спасибо. Как узнать, какую версию XSLT я использую? Я вызываю все это из кода C# (используя класс XslCompiledTransform). –

+0

К сожалению, .NET не поддерживает XSLT 2.0 изначально. Если у вас нет двигателей Saxon.net, XQSharp или других 2.0, вам нужно будет использовать решение 1.0. –

+0

Это сделало трюк, спасибо! –

-1

Если вы знаете, что у вас есть ровно две точки в ваших строк, то вы можете попробовать:

<xsl:value-of select="substring-after(substring-after($str, '.'), '.')" /> 
+0

Это не сработает, если у меня есть произвольное число. в моей строке .. Например: Component.Something.Else.Class.Method (...) –

1

Здесь является более эффективным решением O (N) против O (N^2) для принятого ответа:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output method="text"/> 

<xsl:template match="text()" name="skipAfterDots"> 
    <xsl:param name="pTotalString" select="."/> 
    <xsl:param name="pTotalLength" select="string-length(.)"/> 
    <xsl:param name="pPosition" select="1"/> 
    <xsl:param name="pLastFound" select="-1"/> 

    <xsl:choose> 
    <xsl:when test="$pPosition > $pTotalLength"> 
     <xsl:value-of select="substring($pTotalString, $pLastFound + 1)"/> 
    </xsl:when> 
    <xsl:otherwise> 
     <xsl:variable name="vIsDot" select= 
     "substring($pTotalString, $pPosition, 1) = '.'"/> 

     <xsl:call-template name="skipAfterDots"> 
     <xsl:with-param name="pTotalString" select="$pTotalString"/> 
     <xsl:with-param name="pTotalLength" select="$pTotalLength"/> 
     <xsl:with-param name="pLastFound" select= 
     "$pLastFound * not($vIsDot) + $pPosition * $vIsDot"/> 
     <xsl:with-param name="pPosition" select="$pPosition+1"/> 
     </xsl:call-template> 
    </xsl:otherwise> 
    </xsl:choose> 
</xsl:template> 
</xsl:stylesheet> 

Когда это преобразование применяется на следующий документ XML:

<t>M:Namespace.Class.Method(Something a, Something b)</t> 

разыскиваемых, правильный результат получается:

Method(Something a, Something b) 

Объяснение:

Это решение не содержит призыв к substring-after() функции. Вместо этого на каждом шаге сравнивается только один символ строки для равенства с символом точки. Поскольку не более N символов, это O (N) - линейная сложность.

Наоборот, принятый ответ вызывает функцию substring-after() на каждом шагу. В худшем случае могут быть N точек, и, таким образом, это будет O (N^N) - квадратичная сложность.

Примечание: Мы делаем разумное предположение, что в обоих решениях поиск k-го символа строки является O (1).

+1

Старый комментарий, но мне нужно прокомментировать: принятое решение также O (n). Каждый вызов (правильная реализация) 'contains()' будет просматривать ведущие символы, пока не найдет точку. После того, как он найдет точку, каждый персонаж, на который он смотрел, отброшен. Поэтому он будет рассматривать каждый символ не более одного раза. – nemetroid

+0

@nemetroid: Я имею в виду ''. Если в строке есть 'm' точки, а длина строки' n', то этот алгоритм равен 'O (m * n)' –

+0

Правильно, вы правы. Я не считал, что 'подстрока-после' придется просматривать всю (оставшуюся) строку. – nemetroid

Смежные вопросы