Zahlen formatieren

(Auszug aus "XSLT Kochbuch" von Sal Mangano)

Problem

Sie müssen Zahlen in verschiedenen Formaten anzeigen.

Lösung

Für dieses Problem gibt es zwei allgemeine Lösungen.

Verwenden Sie xsl:decimal-format zusammen mit format-number( )

Das Top-Level-Element xsl:decimal-format stellt eine benannte Formatierungsregel auf, die durch die Funktion format-number() referenziert werden kann, wenn diese Formatregel erforderlich ist. xsl:decimal-format besitzt eine ganze Reihe von Attributen, die die Formatierungsregeln beschreiben. Die folgende Tabelle erläutert die einzelnen Attribute und zeigt in Klammern deren Standardwerte.

Tabelle: Attribute des Elements xsl:decimal-format.

Attribut Zweck
Name Ein optionaler Name für die Regel. Fehlt er, wird diese Regel zur Standardregel. Es kann nur eine Vorgabe geben, und alle Namen müssen eindeutig sein (selbst wenn es einen Unterschied im Importvorrang gibt).
decimal-separator (.) Das Zeichen, das verwendet wird, um den ganzen vom gebrochenen Anteil einer Zahl zu trennen.
grouping-separator (,) Das Zeichen, das verwendet wird, um Gruppen von Ziffern abzutrennen.
infinity (Infinity) Der String, der Unendlichkeit repräsentiert.
minus-sign (-) Das Zeichen, das als Minuszeichen verwendet wird.
NaN (NaN) Der String, der den numerischen Wert »not a number« repräsentiert.
percent (%) Das Prozentzeichen.
per-mille (?) Das Promillezeichen.
zero-digit (0) Das Zeichen, das in einem Formatierungsmuster verwendet wird, um anzuzeigen, wo eine führende oder abschließende Null platziert werden soll. Durch das Setzen dieses Zeichens wird der Ursprung des Zahlensystems wieder zurückgesetzt (siehe das weiter unten aufgeführte Beispiel Eingabe).
digit (#) Das Zeichen, das in einem Formatierungsmuster verwendet wird, um anzuzeigen, wo eine Ziffer auftauchen soll, vorausgesetzt, diese ist signifikant.
pattern-separator (;) Das Zeichen, das in einem Formatierungsmuster verwendet wird, um das positive Teilmuster vom negativen Teilmuster zu trennen.

Die Funktion format-number( ) erwartet die Argumente, die in der folgenden Tabelle gezeigt werden.

Tabelle: Argumente für format-number( ).

Argument Zweck
Wert Der zu formatierende Wert.
Format Ein Formatstring (z. B. #,###.00).
Name (optional) Ein Name eines xsl:decimal-format-Elements.

xsl:number verwenden

Die gebräuchlichste Anwendung von xsl:number besteht darin, Knoten durchzunummerieren. Die Funktion kann aber auch Zahlen formatieren. Soll sie zu diesem Zweck verwendet werden, stehen die in der folgenden Tabelle gezeigten Attribute zur Verfügung.

Tabelle: Attribute von xsl:number.

Name Zweck
value Die zu formatierende Zahl.
format Ein Formatstring (beschrieben in der Diskussion).
lang Ein Sprachcode, wie er durch das Attribut xml:lang definiert wird.
letter-value Muss entweder alphabetic oder traditional lauten und wird verwendet, um zwischen unterschiedlichen Nummerierungsschemata zu unterscheiden.
grouping-separator Ein einzelnes Zeichen, das verwendet wird, um Gruppen zu trennen. In den USA ist beispielsweise das Komma (,) der Standard.
grouping-size Die Anzahl der Ziffern in jeder Gruppe.

Die folgende Tabelle zeigt, wie Formatierungs-Token mit einem Formatattribut verwendet werden.

Tabelle: Beispielverhalten von Formatierungs-Token, die mit einem Formatattribut verwendet werden.

Format-Token Beispielwert Resultierende Ausgabe
1 1 1
1 99 99
01 1 01
001 1 001
a 1 A
a 10 J
a 27 Aa
A 1 A
A 27 AA
i 1 I
i 10 X
I 1 I
I 11 XI

Der Formatstring ist eine alternierende Folge von Format- und Interpunktions-Token. Die Verwendung von mehreren Format-Token ist nur dann sinnvoll, wenn der Wert eine Gruppe von Zahlen enthält.

Diskussion

Mittels der definierten Formatierungsmaschinerie können fast alle Zahlenformatierungsaufgaben erledigt werden.

Formatieren von Zahlen in Spalten bei einer festen Anzahl von Kommastellen

Hier können wir mit führenden und abschließenden Nullen auffüllen. Die führenden Nullen bilden wir anschließend auf Leerzeichen ab, außerdem verwenden wir am Ende ein Minuszeichen. Diese Lösung liefert uns eine hübsche spaltenweise, rechts ausgerichtete Ausgabe, wenn beim endgültigen Ausgabemedium eine Nichtproportionalschrift (eine Schrift, bei der alle Zeichen die gleiche Breite aufweisen) eingesetzt wird. Die folgenden drei Beispiele zeigen konventionellere Auffüllmethoden. Die Beispiele verdeutlichen das Verhalten der Ziffer 0, wenn diese als Formatierungszeichen verwendet wird.

Beispiel: Eingabe.

<numbers>
  <number>10</number>
  <number>3.5</number>
  <number>4.44</number>
  <number>77.7777</number>
  <number>-8</number>
  <number>1</number>
  <number>444</number>
  <number>1.1234</number>
  <number>7.77</number>
  <number>3.1415927</number>
  <number>10</number>
  <number>9</number>
  <number>8</number>
  <number>7</number>
  <number>666</number>
  <number>5555</number>
  <number>-4444444</number>
  <number>22.33</number>
  <number>18</number>
  <number>36.54</number>
  <number>43</number>
  <number>99999</number>
  <number>999999</number>
  <number>9999999</number>
  <number>32</number>
  <number>64</number>
  <number>-64.0001</number>
</numbers>

Beispiel: format-numbers-into-columns.xslt

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text"/>
  <xsl:variable name="numCols" select="3"/>
  <xsl:template match="numbers">
    <xsl:for-each select="number[position( ) mod $numCols = 1]">
      <xsl:apply-templates select=". | following-sibling::number[position( ) &lt; $numCols]" mode="format"/>
      <xsl:text>&#xa;</xsl:text>
    </xsl:for-each>
  </xsl:template>
  <xsl:template match="number" name="format" mode="format">
    <xsl:param name="number" select="."/>
    <xsl:call-template name="leading-zero-to-space">
      <xsl:with-param name="input" select="format-number($number, '0000000.0000 ;0000000.0000- ')"/>
    </xsl:call-template>
  </xsl:template>
  <xsl:template name="leading-zero-to-space">
    <xsl:param name="input"/>
    <xsl:choose>
      <xsl:when test="starts-with($input,'0')">
        <xsl:value-of select="' '"/>
        <xsl:call-template name="leading-zero-to-space">
          <xsl:with-param name="input" select="substring-after($input,'0')"/>
        </xsl:call-template>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="$input"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

Beispiel: Ausgabe.

10.0000 3.5000 4.4400
77.7777   8.0000- 1.0000
444.0000 1.1234 7.7700
3.1416 10.0000 9.0000
8.0000 7.0000 666.0000
5555.0000 4444444.0000- 22.3300
18.0000 36.5400 43.0000
99999.0000 999999.0000 9999999.0000
32.0000 64.0000 64.0001-

Geld nach Art US-amerikanischer Buchhalter formatieren

Das folgende Beispiel zeigt eine Variante des zuvor dargestellten Format-Templates, das Ihren Buchhalter entzücken wird (zumindest, wenn Sie Handelsbeziehungen mit den USA pflegen).

Beispiel: Buchhalterfreundliches Format.

<xsl:template match="number" name="format" mode="format">
  <xsl:param name="number" select="."/>
  <xsl:text> $ </xsl:text>
  <xsl:call-template name="leading-zero-to-space">
    <xsl:with-param name="input" select="format-number($number, ' 0000000.00 ;(0000000.00)')"/>
  </xsl:call-template>
</xsl:template>

Ausgabe:

$ 10.00 $ 3.50 $ 4.44
$ 77.78 $ (8.00) $ 1.00
$ 444.00 $ 1.12 $ 7.77
$ 3.14 $ 10.00 $ 9.00
$ 8.00 $ 7.00 $ 666.00
$ 5555.00 $ (4444444.00) $ 22.33
$ 18.00 $ 36.54 $ 43.00
$ 99999.00 $ 999999.00 $ 9999999.00
$ 32.00 $ 64.00 $ (64.00)

Formatieren von Zahlen für viele europäische Länder

Das folgende Beispiel demonstriert die Verwendung eines benannten Formats.

Beispiel: Das Format European-number.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:str="http://www.ora.com/XSLTCookbook/namespaces/strings">
  <xsl:output method="text"/>
  <!-- Aus Rezept "Einen String n-mal duplizieren" ... -->
  <xsl:include href="../strings/str.dup.xslt"/>
  <xsl:variable name="numCols" select="3"/>
  <xsl:decimal-format name="WesternEurope" decimal-separator="," grouping-separator="."/>
  <xsl:template match="numbers">
    <xsl:for-each select="number[position() mod $numCols = 1]">
      <xsl:apply-templates select=". | following-sibling::number[position() &lt; $numCols]" mode="format"/>
      <xsl:text> </xsl:text>
    </xsl:for-each>
  </xsl:template>
  <xsl:template match="number" name="format" mode="format">
    <xsl:param name="number" select="."/>
    <xsl:call-template name="pad">
      <xsl:with-param name="string" select="format-number($number,'#.###,00','WesternEurope')"/>
    </xsl:call-template>
  </xsl:template>
  <xsl:template name="pad">
    <xsl:param name="string"/>
    <xsl:param name="width" select="16"/>
    <xsl:call-template name="str:dup">
      <xsl:with-param name="input" select="' '"/>
      <xsl:with-param name="count" select="$width - string-length($string)"/>
    </xsl:call-template>
    <xsl:value-of select="$string"/>
  </xsl:template>
</xsl:stylesheet>

Ausgabe:

10,00 3,50 4,44
77,78 -8,00 1,00
444,00 1,12 7,77
3,14 10,00 9,00
8,00 7,00 666,00
5.555,00 -4444.444,00 22,33
18,00 36,54 43,00
99.999,00 999.999,00 9.999.999,00
32,00 64,00 -64,00

Zahlen in römische Ziffern konvertieren

Das folgende Beispiel verwendet xsl:number als Formatierer für römische Ziffern, um Spalten näher zu bezeichnen:

Beispiel: Das Format Roman-numeral.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:str="http://www.ora.com/XSLTCookbook/namespaces/strings">
  <xsl:output method="text" />
  <xsl:include href="../strings/str.dup.xslt"/>
  <xsl:variable name="numCols" select="3"/>
  <xsl:template match="numbers">
    <xsl:for-each select="number[position( ) &lt;= $numCols]">
      <xsl:text> </xsl:text>
      <xsl:number value="position( )" format="I"/><xsl:text> </xsl:text>
    </xsl:for-each>
    <xsl:text>&#xa; </xsl:text>
    <xsl:for-each select="number[position( ) &lt;= $numCols]">
      <xsl:text>———————— </xsl:text>
    </xsl:for-each>
    <xsl:text>&#xa;</xsl:text>
    <xsl:for-each select="number[position( ) mod $numCols = 1]">
      <xsl:call-template name="pad">
        <xsl:with-param name="string">
          <xsl:number value="position( )" format="i"/>
        </xsl:with-param>
        <xsl:with-param name="width" select="4"/>
      </xsl:call-template>|<xsl:text/> <!-- Siehe Rezept "Der Umgang mit Whitespace" -->
      <xsl:apply-templates select=". | following-sibling::number[position( ) &lt; $numCols]" mode="format"/>
      <xsl:text>&#xa;</xsl:text>
    </xsl:for-each>
  </xsl:template>
  <xsl:template match="number" name="format" mode="format">
    <xsl:param name="number" select="." />
    <xsl:call-template name="pad">
      <xsl:with-param name="string" select="format-number(.,'#,###.00')"/>
    </xsl:call-template>
  </xsl:template>
  <xsl:template name="pad">
    <xsl:param name="string"/>
    <xsl:param name="width" select="16"/>
    <xsl:call-template name="str:dup">
      <xsl:with-param name="input" select="' '"/>
      <xsl:with-param name="count" select="$width - string-length($string)"/>
    </xsl:call-template>
    <xsl:value-of select="$string"/>
  </xsl:template>
</xsl:stylesheet>

Ausgabe:

I II III
---------------------- ---------------------- ----------------------
i   | 10.00 3.50 4.44
ii   | 77.78 -8.00 1.00
iii   | 444.00 1.12 7.77
iv   | 3.14 10.00 9.00
v   | 8.00 7.00 666.00
vi   | 5.555.00 -4444.444.00 22.33
vii   | 18.00 36.54 43.00
viii   | 99.999.00 999.999.00 9.999.999.00
ix   | 32.00 64.00 -64.00

Spaltennummern wie in einer Tabellenkalkulation erzeugen

Tabellenkalkulationen nummerieren Spalten in alphabetischer Reihenfolge mit A, B, C ... ZZ. Mit xsl:number können wir das auch erreichen (siehe folgendes Beispiel).

Beispiel: Spaltennummern nach Art einer Tabellenkalkulation.

<xsl:template match="numbers">
  <xsl:for-each select="number[position( ) &lt;= $numCols]">
    <xsl:text> </xsl:text>
    <xsl:number value="position()" format="A"/><xsl:text> </xsl:text>
    <xsl:text> </xsl:text>
  </xsl:for-each>
  <xsl:text>&#xa;</xsl:text>
  <xsl:for-each select="number[position() &lt;= $numCols]">
    <xsl:text> ---------------- </xsl:text>
  </xsl:for-each>
  <xsl:text>&#xa;</xsl:text>
  <xsl:for-each select="number[position() mod $numCols = 1]">
    <xsl:value-of select="position()"/><xsl:text>|</xsl:text>
    <xsl:apply-templates select=". | following-sibling::number[position() &lt; $numCols]" mode="format"/>
    <xsl:text>&#xa;</xsl:text>
  </xsl:for-each>
</xsl:template>
<xsl:template match="number" name="format" mode="format">
  <xsl:param name="number" select="." />
  <xsl:call-template name="pad">
    <xsl:with-param name="string" select="format-number(.,'#,###.00')"/>
  </xsl:call-template>
  <xsl:text> </xsl:text>
</xsl:template>

Ausgabe:

A B C
---------------------- ---------------------- ----------------------
1   | 10.0000 3.5000 4.4400
2   | 77.7777 8.0000- 1.0000
3   | 444.0000 1.1234 7.7700
4   | 3.1416 10.0000 9.0000
5   | 8.0000 7.0000 666.0000
6   | 5555.0000 4444444.0000- 22.3300
7   | 18.0000 36.5400 43.0000
8   | 99999.0000 999999.0000 9999999.0000
9   | 32.0000 64.0000 64.0001-

Formatieren von Zahlen mit Hilfe arabischer Zeichen

Zahlenformate aus anderen Sprachen können verwendet werden, indem die Ziffer Null in xsl:decimal-format auf die Null dieser Sprache gesetzt wird. Dieses Beispiel benutzt das Unicode-Zeichen 0x660 (arabisch-indische Ziffer Null):

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:str="http://www.ora.com/XSLTCookbook/namespaces/strings">
  <xsl:output method="text" encoding="UTF-8"/>
  <xsl:include href="../strings/str.dup.xslt"/>
  <!-- Dies gibt an, dass Null beim Zeichen 0x660 beginnt, was impliziert, dass Eins 0x661 ist usw. -->
  <xsl:decimal-format name="Arabic" zero-digit="&#x660;"/>
  <xsl:template match="numbers">
    <xsl:for-each select="number">
      <xsl:call-template name="pad">
        <xsl:with-param name="string" select="format-number(.,'#,###.00')"/>
      </xsl:call-template> = <xsl:text/>
      <xsl:value-of select="format-number(.,'#,###.&#x660;&#x660;','Arabic')"/>
      <xsl:text>&#xa;</xsl:text>
    </xsl:for-each>
  </xsl:template>
  <xsl:template name="pad">
    <xsl:param name="string"/>
    <xsl:param name="width" select="16"/>
    <xsl:value-of select="$string"/>
    <xsl:call-template name="str:dup">
      <xsl:with-param name="input" select="' '"/>
      <xsl:with-param name="count" select="$width - string-length($string)"/>
    </xsl:call-template>
  </xsl:template>
</xsl:stylesheet>

Hier ist eine Abbildung der Ausgabe dieses Codes:

Ausgabe

  

zum Seitenanfang

<< zurück vor >>

 

 

 

Tipp der data2type-Redaktion:
Zum Thema XSLT bieten wir auch folgende Schulungen zur Vertiefung und professionellen Fortbildung an:

Copyright © 2006 O'Reilly Verlag GmbH & Co. KG
Für Ihren privaten Gebrauch dürfen Sie die Online-Version ausdrucken.
Ansonsten unterliegt dieses Kapitel aus dem Buch "XSLT Kochbuch" denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.

O'Reilly Verlag GmbH & Co. KG, Balthasarstraße 81, 50670 Köln, kommentar(at)oreilly.de