Получение подстроки ПОСЛЕ последнего вхождения символа в XSLT

У меня есть строка в файле XML, которая выглядит примерно так:

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

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

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

Метод (Нечто а, Нечто б)

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

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

 Marc Stober13 июн. 2012 г., 12:17

Ответы на вопрос(3)

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

<xsl:value-of select="substring-after(substring-after($str, '.'), '.')" /> 
 lysergic-acid31 янв. 2012 г., 12:46
Это не будет работать, если у меня есть произвольное количество. в моей строке .. Например: Component.Something.Else.Class.Method (...)

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

<xsl:stylesheet version="1..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).

 Dimitre Novatchev24 июл. 2012 г., 01:33
@nemetroid: мое объяснение вводило в заблуждение (упомянутоcontains() вместоsubstring-after()). Исправлено сейчас.
 nemetroid22 июл. 2012 г., 21:51
Старый комментарий, но я чувствую необходимость комментировать: принятым решением также является O (n). Каждый вызов (вменяемая реализация)contains() будет просматривать ведущих символов, пока не найдет точку. После того, как он находит точку, все символы, на которые он смотрел до сих пор, отбрасываются. Поэтому он будет смотреть на каждого персонажа не более одного раза.
 Dimitre Novatchev22 июл. 2012 г., 22:12
@nemetroid: я имею в виду<xsl:with-param name="string" select="substring-after($string, $delimiter)"/>, Если естьm точки в строке и длина строкиnто этот алгоритмO(m*n)
 nemetroid24 июл. 2012 г., 01:01
Правильно, вы правы. Я не учел чтоsubstring-after придется просматривать всю (оставшуюся) строку.
Решение Вопроса

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

      <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('M:Namespace.Class.Method(Something a, Something b)','\.')[last()]
 lysergic-acid31 янв. 2012 г., 13:23
Это помогло, спасибо!
 lysergic-acid31 янв. 2012 г., 12:59
Благодарю. Как мне узнать, какую версию XSLT я использую? Я вызываю все это из кода C # (используя класс XslCompiledTransform).
 Mads Hansen31 янв. 2012 г., 13:04
К сожалению, .NET изначально не поддерживает XSLT 2.0. Если у вас нет Saxon.net, XQSharp или других движков 2.0, вам нужно будет использовать решение 1.0.

Ваш ответ на вопрос