2010-05-06 13 views
16

Ho bisogno di scorrere i caratteri in una stringa per creare una struttura XML.XSLT 1.0: Iterate su caratteri in una stringa

Attualmente, sto facendo questo:

<xsl:template name="verticalize"> 
    <xsl:param name="text">Some text</xsl:param> 
    <xsl:for-each select="tokenize(replace(replace($text,'(.)','$1\\n'),'\\n$',''),'\\n')"> 
     <xsl:element name="para"> 
      <xsl:value-of select="."/> 
     </xsl:element> 
    </xsl:for-each> 
</xsl:template> 

Questo produce qualcosa di simile:

<para>S</para> 
<para>o</para> 
<para>m</para> 
<para>e</para> 
<para> </para> 
<para>t</para> 
<para>e</para> 
<para>x</para> 
<para>t</para> 

Questo funziona bene con XPath 2.0. Ma ho bisogno di applicare lo stesso trattamento in un ambiente XPath 1.0, in cui il metodo replace() non è disponibile.

Conosci un modo per raggiungere questo obiettivo?

+0

Ottima domanda (+1). Vedi la mia risposta per una soluzione XSLT 2.0, che è anche la più breve finora. :) –

+0

Vedere anche la mia soluzione XSLT 1.0. :) –

+0

@Dimitre, se non fosse il più breve, non sarebbe stato inutile usare XSLT 2.0 ;-) – Lucero

risposta

17
<xsl:template name="letters"> 
    <xsl:param name="text" select="'Some text'" /> 
    <xsl:if test="$text != ''"> 
    <xsl:variable name="letter" select="substring($text, 1, 1)" /> 
    <para><xsl:value-of select="$letter" /></para> 
    <xsl:call-template name="letters"> 
     <xsl:with-param name="text" select="substring-after($text, $letter)" /> 
    </xsl:call-template> 
    </xsl:if> 
</xsl:template> 
+0

Tomalak, tu sei il mio eroe XSLT. – glmxndr

+0

+1 per la soluzione ricorsiva in coda! –

+0

La ricorsione in coda è piacevole finché non si ottiene un file XML gigante a cui piace dare errori di stackoverflow. –

8

Se la lunghezza della stringa non è eccessiva, è possibile utilizzare un modello chiamato ricorsivamente per ottenere ciò, passando l'indice del carattere da elaborare come parametro nel modello.

Come così:

<xsl:template name="verticalize"> 
    <xsl:param name="text">Some text</xsl:param> 
    <xsl:param name="index" select="1" /> 
    <xsl:if test="string-length($text) &gt;= $index"> 
     <xsl:element name="para"> 
      <xsl:value-of select="substring($text, $index, 1)"/> 
     </xsl:element> 
     <xsl:call-template name="verticalize"> 
      <xsl:with-param name="text" select="$text" /> 
      <xsl:with-param name="index" select="$index+1" /> 
     </xsl:call-template> 
    </xsl:if> 
</xsl:template> 

Se la stringa è più lungo di quello, è possibile utilizzare un approccio simile, ma con un algoritmo divide et impera, in modo da avere una profondità massima di ricorsione di log2 (stringa -length), in questo modo:

<xsl:template name="verticalize"> 
    <xsl:param name="text">Some text</xsl:param> 
    <xsl:param name="left" select="1" /> 
    <xsl:param name="right" select="string-length($text)" /> 
    <xsl:choose> 
     <xsl:when test="$left = $right"> 
      <xsl:element name="para"> 
       <xsl:value-of select="substring($text, $left, 1)"/> 
      </xsl:element> 
     </xsl:when> 
     <xsl:when test="$left &lt; $right"> 
      <xsl:variable name="middle" select="floor(($left+$right) div 2)" /> 
      <xsl:call-template name="verticalize"> 
       <xsl:with-param name="text" select="$text" /> 
       <xsl:with-param name="left" select="$left" /> 
       <xsl:with-param name="right" select="$middle" /> 
      </xsl:call-template> 
      <xsl:call-template name="verticalize"> 
       <xsl:with-param name="text" select="$text" /> 
       <xsl:with-param name="left" select="$middle+1" /> 
       <xsl:with-param name="right" select="$right" /> 
      </xsl:call-template> 
     </xsl:when> 
    </xsl:choose> 
</xsl:template> 
+0

Grazie mille per questa risposta dettagliata. – glmxndr

+0

Prego, ma perché non mi hai assegnato i punti di risposta? Avevo 3 minuti prima di Tomalak e ho già pubblicato il primo campione ... – Lucero

+0

@Lucero: hai postato una risposta standard senza codice. Avevo il mio codice pronto prima di te. ;) Ma ottieni +1 da me per la variante divide/conquista. Un suggerimento però: la ricorsione sul resto della sola stringa ('substring-after()') minimizza l'utilizzo della memoria. La tua ricorsità passa una copia dell'intera stringa in ogni fase, e ciò si aggiunge fino allo svolgimento dello stack. – Tomalak

7

Un XSLT 2.0 Soluzione:

<xsl:stylesheet version="2.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 

<xsl:variable name="vText" select="'Some Text'"/> 

<xsl:template match="/"> 
    <xsl:for-each select="string-to-codepoints($vText)"> 
     <para><xsl:sequence select="codepoints-to-string(.)"/></para> 
    </xsl:for-each> 
</xsl:template> 
</xsl:stylesheet> 

Per quelli di voi, imparare XSLT 2.0/XPath 2.0, faccio notare:

  1. L'uso del XPath 2.0 standard funzioni string-to-codepoints() e codepoints-to-string().

  2. In XSLT 2.0 il valore dell'attributo select di <xsl:for-each> può essere una sequenza di elementi, non solo nodi.

+0

Bella soluzione. Tuttavia, non funzionerà come previsto quando si combinano i caratteri per rappresentare gli accenti ecc .: http://en.wikipedia.org/wiki/Combining_character – Lucero

+0

@Lucero: È interessante. Potresti darmi un esempio di "carattere combinato", in modo che possa capire meglio? Inoltre, mi sembra che nessuna delle altre soluzioni possa funzionare in questo caso. –

+0

@Dimitre, per i campioni dare un'occhiata a Wikipedia: http://en.wikipedia.org/wiki/Unicode_normalization Non sono sicuro se le altre soluzioni funzionerebbero o meno; Mi aspetterei che le funzioni stringa funzionino su caratteri composti, poiché tutto il resto spezzerebbe i singoli caratteri (testuali). Dovrei controllare le specifiche XPath/XSLT su come funzionano. – Lucero

2

Un XSLT 1.0 soluzione utilizzando FXSL

La FXSL library offre una serie di funzioni generiche per l'elaborazione lista. Quasi tutti hanno un analogo per operare su stringhe (riguardo una stringa come una lista di caratteri).

Ecco un esempio utilizzando la str-foldl funzione/template:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:dvc-foldl-func="dvc-foldl-func" 
exclude-result-prefixes="xsl dvc-foldl-func" 
> 

    <xsl:import href="dvc-str-foldl.xsl"/> 

    <dvc-foldl-func:dvc-foldl-func/> 
    <xsl:variable name="vFoldlFun" select="document('')/*/dvc-foldl-func:*[1]"/> 
    <xsl:output encoding="UTF-8" omit-xml-declaration="yes"/> 

    <xsl:template match="/"> 

     <xsl:call-template name="dvc-str-foldl"> 
     <xsl:with-param name="pFunc" select="$vFoldlFun"/> 
     <xsl:with-param name="pStr" select="123456789"/> 
     <xsl:with-param name="pA0" select="0"/> 
     </xsl:call-template> 
    </xsl:template> 

    <xsl:template match="dvc-foldl-func:*"> 
     <xsl:param name="arg1" select="0"/> 
     <xsl:param name="arg2" select="0"/> 

     <xsl:value-of select="$arg1 + $arg2"/> 
    </xsl:template> 

</xsl:stylesheet> 

Questa trasformazione calcola la somma dei caratteri nella stringa passato come parametro $pStr e produce il risultato corretto:

E utilizzando il modello/funzione str-map abbiamo il seguente facile e d soluzione a breve:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:testmap="testmap" 
exclude-result-prefixes="xsl testmap" 
> 
    <xsl:import href="str-dvc-map.xsl"/> 

    <!-- to be applied on any xml source --> 

    <testmap:testmap/> 

    <xsl:output omit-xml-declaration="yes" indent="yes"/> 

    <xsl:template match="/"> 
    <xsl:variable name="vTestMap" select="document('')/*/testmap:*[1]"/> 
    <xsl:call-template name="str-map"> 
     <xsl:with-param name="pFun" select="$vTestMap"/> 
     <xsl:with-param name="pStr" select="'Some Text'"/> 
    </xsl:call-template> 
    </xsl:template> 

    <xsl:template name="split" match="*[namespace-uri() = 'testmap']"> 
     <xsl:param name="arg1"/> 

     <para><xsl:value-of select="$arg1"/></para> 
    </xsl:template> 

</xsl:stylesheet> 

Quando applicato su qualsiasi file XML (non utilizzato), l'ha voluto, risultato corretto è prodotto:

<para>S</para> 
<para>o</para> 
<para>m</para> 
<para>e</para> 
<para> </para> 
<para>T</para> 
<para>e</para> 
<para>x</para> 
<para>t</para>