2012-01-31 10 views
7

tengo una cadena en un archivo XML que se parece a esto:Conseguir una subcadena después de la última aparición de un caracter en XSLT

M: Namespace.Class.Method (Algo un, Algo b)

El número de caracteres de período (.) Es abritrary, lo que significa que puede ser solo 2 como en este ejemplo, pero puede ser más.

Me gustaría usar XSLT para obtener una subcadena de esta cadena del último '.' carácter, por lo que sólo se quedará con:

Método (Algo un, Algo b)

no pude lograr esto mediante la subcadena estándar/subcadena-después de las funciones.

¿Hay una manera fácil de hacer esto?

+0

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

Respuesta

19

En XSLT 1.0 se tendrá que utilizar una plantilla recursiva, así:

<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> 

e invocar así:

<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> 

En XSLT 2.0, puede utilizar la función detokenize() y sólo tiene que seleccionar el último elemento de la secuencia:

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

Gracias. ¿Cómo sé qué versión de XSLT estoy usando? Invoco todo esto desde el código C# (usando la clase XslCompiledTransform). –

+0

Desafortunadamente .NET no es compatible con XSLT 2.0 de forma nativa. A menos que tenga Saxon.net, XQSharp u otros motores 2.0, necesitará usar una solución 1.0. –

+0

Esto hizo el truco, gracias! –

-1

Si sabe que tiene exactamente dos puntos en sus cadenas continuación, puede probar:

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

Esto no funcionará si tengo un número arbitrario de. en mi cadena ... Por ejemplo: Component.Something.Else.Class.Method (...) –

1

Aquí es una solución más eficiente O (N) vs. O (N^2) para la respuesta aceptada:

<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> 

Cuando se aplica esta transformación en el siguiente documento XML:

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

el, resultado correcto querido se produce:

Method(Something a, Something b) 

Explicación:

Esta solución no contiene ninguna llamada a la funciónsubstring-after(). En su lugar, en cada paso solo se compara el carácter de la cadena para la igualdad con el carácter de punto. Debido a que hay como mucho N caracteres, esta es O (N) - complejidad lineal.

Por el contrario, la respuesta aceptada llama a la función substring-after() en cada paso. En el peor de los casos, podría haber N puntos y, por lo tanto, esto sería O (N^N) - complejidad cuadrática.

Nota: Suponemos razonablemente que en ambas soluciones la ubicación del carácter k-ésimo de una cadena es O (1).

+1

Comentario anterior, pero siento la necesidad de comentar: la solución aceptada también es O (n). Cada llamada a (una implementación correcta de) 'contains()' mirará a través de los caracteres iniciales hasta que encuentre un punto. Después de encontrar un punto, todos los personajes que ha examinado hasta el momento se descartan. Por lo tanto, mirará a cada personaje como máximo una vez. – nemetroid

+0

@nemetroid: me refiero a ''. Si hay 'm' puntos en la cadena y la longitud de la cadena es' n', entonces este algoritmo es 'O (m * n)' –

+0

Correcto, estás en lo correcto. No consideré que 'substring-after' tendrá que examinar toda la cadena (restante). – nemetroid

Cuestiones relacionadas