How to find all numbers in a string

2019-06-03 16:24发布

问题:

Ich versuche mit einer Funktion sämtliche Zahlen aus einem Element oder String zu ermitteln. Dabei soll die Anzahl der Zahlen und ihre Stelligkeit egal sein. Folgende Funktion habe ich bislang geschrieben:

<xsl:function name="itp:find_num">
<xsl:param name="tmp"/>
<xsl:if test="matches($tmp,'\d+')">
    <xsl:analyze-string select="$tmp" regex="{'\d+'}">
        <xsl:matching-substring>
             <xsl:sequence select="."/>
        </xsl:matching-substring>
        <xsl:non-matching-substring>
            <xsl:value-of select="''"/>
        </xsl:non-matching-substring>
    </xsl:analyze-string>
</xsl:if>
</xsl:function>

Beispiel XML:

<address>street 12, 12345 town<address>

Bei dem Funktionsaufruf soll dann die entsprechende Zahl ausgewählt werden können:

...select="itp:find_num(address)[2]"/>

Zum Beispiel die 2 für die Postleitzahl. Das Problem ist nun, dass in der Sequence auch leere Werte stehen, so dass ich in der Praxis die Postleitzahl nur mit [4] erreiche.

Gibt es eine elegantere Möglichkeit meine Problem zu lösen? Und wenn nicht, wie lösche ich die leeren Elemente aus der Sequence??

Now in Englisch :-)

I'm trying to find all numbers in an element oder string. It shouldn't matter how many numbers are available or at which position they are in the string.

Here is my function:

<xsl:function name="itp:find_num">
<xsl:param name="tmp"/>
<xsl:if test="matches($tmp,'\d+')">
    <xsl:analyze-string select="$tmp" regex="{'\d+'}">
        <xsl:matching-substring>
        <xsl:if test=". != ''">
            <xsl:sequence select="."/>
        </xsl:if>
        </xsl:matching-substring>
        <xsl:non-matching-substring>
            <xsl:value-of select="''"/>
        </xsl:non-matching-substring>
    </xsl:analyze-string>
</xsl:if>
</xsl:function>

Example XML

<address>street 12, 12345 town<address>

When I call the function I want to choose, which number I want to pick:

...select="itp:find_num(address)[2]"/>

Par example [2] for the postal code. The Problem I have is, that there are empty elements in the sequence, so that I have to choose [4] to get the postal code.

Is there a easier way to solve my problem? Or is there a way to remove all empty elements in that sequence??

Thanks :-)

回答1:

I would write the function as

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:mf="http://example.org/mf"
  exclude-result-prefixes="xs mf">

<xsl:function name="mf:find_num" as="xs:integer*">
  <xsl:param name="input" as="xs:string"/>
  <xsl:analyze-string select="$input" regex="[0-9]+">
    <xsl:matching-substring>
      <xsl:sequence select="xs:integer(.)"/>
    </xsl:matching-substring>
  </xsl:analyze-string>
</xsl:function>

<xsl:template match="address">
  <xsl:value-of select="mf:find_num(.)" separator=", "/>
</xsl:template>

</xsl:stylesheet>

Of course converting to xs:integer is optional, if you want the function to return a sequence of strings containing digits you would simply change it to do

<xsl:function name="mf:find_num" as="xs:string*">
  <xsl:param name="input" as="xs:string"/>
  <xsl:analyze-string select="$input" regex="[0-9]+">
    <xsl:matching-substring>
      <xsl:sequence select="."/>
    </xsl:matching-substring>
  </xsl:analyze-string>
</xsl:function>


回答2:

There is no need for complex xsl:analyze-string processing at all.

This XPath one-liner:

   for $i in tokenize($pStr, '[^0-9]+')[.]
    return xs:integer($i)

produces the wanted sequence of the integers in the string:

12 12345

Here is a complete transformation:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:my="my:my">
 <xsl:output method="text"/>

 <xsl:template match="/*">
  <xsl:sequence select="my:nums(.)"/>
 </xsl:template>

 <xsl:function name="my:nums" as="xs:integer*">
  <xsl:param name="pStr" as="xs:string"/>

  <xsl:sequence select=
  "for $i in tokenize($pStr, '[^0-9]+')[.]
    return xs:integer($i)"/>
 </xsl:function>
</xsl:stylesheet>

When this transformation is applied on the provided XML document:

<address>street 12, 12345 town</address>

the wanted, correct result is produced:

12 12345

Even simpler, XPath 3.0 one-liner:

tokenize($pStr, '[^0-9]+')[.] ! xs:integer(.)