Römische Ziffern in Zahlen konvertieren

(Auszug aus "XSLT Kochbuch" von Sal Mangano)

Problem

Sie müssen eine römische Ziffer in eine Zahl umwandeln.

Lösung

Römische Zahlen verwenden kein Stellenwertsystem, stattdessen wird die Zahl erzeugt, indem der feste Wert der angegebenen römischen Ziffernzeichen addiert oder subtrahiert wird. Besitzt das nachfolgende Zeichen einen niedrigeren oder gleichen Wert, addieren Sie, ansonsten subtrahieren Sie:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:math="http://www.ora.com/XSLTCookbook/math">
  <ckbk:romans>
    <ckbk:roman value="1">i</ckbk:roman>
    <ckbk:roman value="1">I</ckbk:roman>
    <ckbk:roman value="5">v</ckbk:roman>
    <ckbk:roman value="5">V</ckbk:roman>
    <ckbk:roman value="10">x</ckbk:roman>
    <ckbk:roman value="10">X</ckbk:roman>
    <ckbk:roman value="50">l</ckbk:roman>
    <ckbk:roman value="50">L</ckbk:roman>
    <ckbk:roman value="100">c</ckbk:roman>
    <ckbk:roman value="100">C</ckbk:roman>
    <ckbk:roman value="500">d</ckbk:roman>
    <ckbk:roman value="500">D</ckbk:roman>
    <ckbk:roman value="1000">m</ckbk:roman>
    <ckbk:roman value="1000">M</ckbk:roman>
  </ckbk:romans>
  <xsl:variable name="ckbk:roman-nums" select="document('')/*/*/ckbk:roman"/>
  <xsl:template name="ckbk:roman-to-number">
    <xsl:param name="roman"/>  
    <xsl:variable name="valid-roman-chars">
      <xsl:value-of select="document('')/*/ckbk:romans"/>
    </xsl:variable>   
    <xsl:choose>
      <!-- wird wahr, wenn es Zeichen in dem String gibt, die keine römischen Zahlen sind -->
      <xsl:when test="translate($roman,$valid-roman-chars,'')">NaN</xsl:when>
      <xsl:otherwise>
        <xsl:call-template name="ckbk:roman-to-number-impl">
          <xsl:with-param name="roman" select="$roman"/>
        </xsl:call-template>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
  <xsl:template name="ckbk:roman-to-number-impl">
    <xsl:param name="roman"/>
    <xsl:param name="value" select="0"/>
    <xsl:variable name="len" select="string-length($roman)"/>
    <xsl:choose>
      <xsl:when test="not($len)">
        <xsl:value-of select="$value"/>
      </xsl:when>
      <xsl:when test="$len = 1">
        <xsl:value-of select="$value + $ckbk:roman-nums[. = $roman]/@value"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:variable name="roman-num" select="$ckbk:roman-nums[. = substring($roman, 1, 1)]"/>
        <xsl:choose>
          <xsl:when test="$roman-num/following-sibling::ckbk:roman = substring($roman, 2, 1)">
            <xsl:call-template name="ckbk:roman-to-number-impl">
              <xsl:with-param name="roman" select="substring($roman,2,$len - 1)"/>
              <xsl:with-param name="value" select="$value - $roman-num/@value"/>
            </xsl:call-template>
          </xsl:when>
          <xsl:otherwise>
            <xsl:call-template name="ckbk:roman-to-number-impl">
              <xsl:with-param name="roman" select="substring($roman,2,$len - 1)"/>
              <xsl:with-param name="value" select="$value + $roman-num/@value"/>
            </xsl:call-template>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

Diskussion

Das Element xsl:number bietet eine bequeme Methode, um Zahlen in römische Ziffern umzuwandeln. Falls Sie jedoch römische Ziffern in Zahlen umwandeln wollen, sind Sie auf sich gestellt. Das bereits gezeigte rekursive Template ist sehr direkt und ähnelt demjenigen, das schon in Jeni Tennisons Buch XSLT and XPath on the Edge (M&T Books, 2001) vorgestellt wurde.

Es gibt zwei kleinere Vorbehalte, diese sollten aber in den meisten Fällen keine Probleme verursachen. Der erste besagt, dass die gezeigte Lösung nicht funktioniert, wenn die römischen Ziffern in gemischter Schreibweise vorliegen (z. B. IiI). Solche eigenartigen Strings werden in vernünftigen Datenquellen kaum auftauchen, doch dieser Code würde eine solche Eingabe weder abweisen noch den »korrekten« Wert liefern. Wird Code hinzugefügt, um diese Fälle in eine einheitliche Schreibweise zu überführen, könnte man den Code entweder abweisen oder die gemischten römischen Ziffern richtig verarbeiten.

Der zweite Vorbehalt hat mit der Tatsache zu tun, dass es keine standardisierte römische Darstellung von Zahlen größer als 1000 gibt. Saxon und Xalan behalten Ms beieinander, ein anderer Prozessor könnte aber anders vorgehen.

Falls Sie es aus irgendeinem Grund ablehnen, Daten über römische Ziffern im Stylesheet zu speichern, dann dekodiert das folgende XPath 1.0-Konstrukt eine römische Ziffer:

<xsl:variable name="roman-value" select="($c = 'i' or $c = 'I') * 1 + ($c = 'v' or $c = 'V') * 5 + ($c = 'x' or $c = 'X') * 10 + ($c = 'l' or $c = 'L') * 50 + ($c = 'c' or $c = 'C') * 100 + ($c = 'd' or $c = 'D') * 500 + ($c = 'm' or $c = 'M') * 1000)"/>

In XSLT 2.0 können Sie einen geschachtelten If-then-else-Ausdruck verwenden oder eine Suche innerhalb einer Sequenz vornehmen:

<xsl:variable name="roman-value" select="(1,5,10,50,100,500,1000) [index-of(('I', 'V', 'X', 'L', 'C', 'D', 'M'),upper-case($c))]"/>

  

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