Kommaseparierte Reihe von Werten (CSV) mit while-Schleife verarbeiten
(Stand: 15.08.2023)
→ Userpages - Wolfgang (WolfgangK)
In diesem Scriptbeispiel wird gezeigt, wie alle Elemente einer kommaseparierten Reihe von Werten (CSV-Reihe) in einer while-Schleife verarbeitet werden können.
In Rocrail gibt es schon seit längerem die Möglichkeit, über sogenannten indizierten Benutzervariablen https://wiki.rocrail.net/doku.php?id=text-gen-de#benutzer-variablen auf die Elemente einer kommaseparierten Wertereihe1) zuzugreifen. Standen bisher 10 solcher indizierten Benutzervariablen zur Verfügung, so kann seit Rocrail-Version 2.1.3088 der Index die Werte 0 bis 99 annehmen. Der Gedanke, eine CSV-Reihe in einer while-Schleife verarbeiten zu wollen, ist daher recht naheliegend. Eine erste Lösung hatte ich am 14.04.2023 vorgestellt, wo ich diese Aufgabe mit Hilfe einer Funktion erledigt hatte. Am 09.08.2023 hat Berthold (Babbel) dann eine deutlich kompaktere und elegantere Lösung im Rocrail-Spätschoppen2) vorgeführt, die ich auch hier an Stelle meiner alten Version übernommen habe.
Der Standardweg, um z. B. auf ein Element der Variable @vr_csv-Reihe zuzugreifen, ist ja folgender:
<?xml version="1.0" encoding="UTF-8"?> <xmlscript> <vr id="vr_csv-Reihe" text="Wert_1,Wert_2,Wert_3,Wert_4"/> <vr id="vr_csv-Element_1" text="@@0vr_csv-Reihe"/> <vr id="vr_csv-Element_3" text="@@2vr_csv-Reihe"/> <trace text="@vr_csv-Element_1 und @vr_csv-Element_3"/> </xmlscript>
Die Ausgabe im Trace sieht dann wie folgt aus3):
Wert_1 und Wert_3
In einer while-Schleife muss also in der Anweisung
<vr id="vr_csv-Element" text="@@2vr_csv-Reihe"/>
die »2« hinter »@@« durch einen Zähler (im Folgenden mit »vr_csv_Position« bezeichnet) ersetzt werden, der während des Schleifendurchlaufs hochgezählt wird.
Ein einfacher Lösungsversuch wie
<vr id="vr_csv_Position" value="2"/> <vr id="vr_csv-Element" text="@@ #vr_csv_Position vr_csv-Reihe"/> <trace text="vr_csv-Element: @vr_csv-Element"/>
funktioniert leider nicht, wie im Trace zu sehen ist:
vr_csv-Element: 2 vr_csv-Reihe
Die beiden »@@« werden ignoriert und zwischen der »2« und dem Variablennamen steht ein Leerzeichen. Bertholds erste Idee war zwei weitere Variablen einzuführen. Eine erste Variable (im Folgenden mit »vr_doppeltes-At-Zeichen« bezeichnet), in der beide at-Zeichen »@@« gespeichert werden, und eine zweite (»vr_Name_csv-Reihe«), in der der Name der Variablen gespeichert wird, die die kommaseparierte Liste enthält. Dann konnten mit Hilfe des Format-Attributs alle drei Variablen ohne dazwischen stehende Leerzeichen aneinander gehängt werden.
<vr id="vr_doppeltes-At-Zeichen" text="@@"/> <vr id="vr_csv_Position" value="2"/> <vr id="vr_Name_csv-Reihe" text="vr_csv-Reihe"/> <vr id="vr_csv-Element" format="%s%d%s" text="@vr_doppeltes-At-Zeichen #vr_csv_Position @vr_Name_csv-Reihe"/> <trace text="vr_csv-Element: @vr_csv-Element"/>
Wie im Trace zu sehen ist
vr_csv-Element: (null)2vr_csv-Reihe
bleibt aber noch das Problem, dass die beiden »@@« jetzt als Beginn einer Variablen interpretiert werden. Da es diese Variable aber nicht gibt und deshalb auch kein Wert zur Verfügung steht, wird in solchen Fällen »(null)« angezeigt. Um dies zu verhindern, hatte er eine zweite, pfiffige Idee: Er setzte vor die beiden »@@« noch ein weiteres Zeichen4), das er dann nach dem Zusammenfügen aller drei Variablen wieder entfernte.
<vr id="vr_doppeltes-At-Zeichen" text="\@@"/> <vr id="vr_Name_csv-Reihe" text="vr_csv-Reihe"/> <vr id="vr_csv-Element" format="%s%d%s" text="@vr_doppeltes-At-Zeichen #vr_csv_Position @vr_Name_csv-Reihe"/> <trace text="vr_csv-Element: @vr_csv-Element"/> <vr id="vr_csv-Element" text="@vr_csv-Element" start="1"/> <trace text="vr_csv-Element: @vr_csv-Element"/>
Durch das zusätzliche Zeichen »\« werden die drei Zeichen »\@@« nicht mehr als eine Variable sondern als ein »gewöhnliches« Wort interpretiert.5) Jetzt wird also zunächst genau das, was in den drei Variablen steht, in einer Variablen zusammengesetzt. Wenn dann das erste Zeichen mit »start="1"«6) entfernt wird, bleibt »@@2vr_csv-Reihe« übrig. Im Trace steht dann also:
vr_csv-Element: \@@2vr_csv-Reihe vr_csv-Element: Wert_3
Das Zusammenfügen der Variablen und das Entfernen des ersten Buchstabens kann auch in einem Befehl erfolgen.
<vr id="vr_csv-Element" format="%s%d%s" text="@vr_doppeltes-At-Zeichen #vr_csv_Position @vr_Name_csv-Reihe" start="1"/>
Nach diesen Vorüberlegungen kann jetzt auf folgende Weise eine while-Schleife gestaltet werden:
<?xml version="1.0" encoding="UTF-8"?> <xmlscript> <vr id="vr_csv-Reihe" text="Wert_1,Wert_2,Wert_3,Wert_4"/> <vr id="vr_csv-Element" text=""/> <vr id="vr_doppeltes-At-Zeichen" text="\@@"/> <vr id="vr_Name_csv-Reihe" text="vr_csv-Reihe"/> <vr id="vr_csv_Position" value="0"/> <vr id="vr_csv-Element" text="@@0vr_csv-Reihe"/> <while condition="@vr_csv-Element - (null)" max="100"> <trace text="@vr_csv-Element"/> <vr id="vr_csv_Position" value="#vr_csv_Position + 1"/> <vr id="vr_csv-Element" format="%s%d%s" text="@vr_doppeltes-At-Zeichen #vr_csv_Position @vr_Name_csv-Reihe" start="1"/> </while> </xmlscript>
Die obige while-Schleife gibt also im Trace nacheinander die Elemente von »vr_csv-Reihe« aus:
Wert_1 Wert_2 Wert_3 Wert_4
Sie wird abgebrochen, wenn die Variable »vr_csv-Element« keinen Wert mehr enthält. Dies ist der Fall, wenn die Variable »vr_csv_Position« den Wert »4« hat und dann in der Variablen »vr_csv-Element« der Wert von »@@4vr_csv-Reihe« gespeichert werden soll, den es aber nicht gibt. Wie wir schon oben gesehen haben, wird dann an Stelle des nicht existierenden Variableninhalts »(null)« zurück gegeben.
Für den Fall, dass »@vr_csv-Reihe« auch mal gar kein Element enthält, wird das obige Script noch um eine entsprechende if-Abfrage erweitert:
<?xml version="1.0" encoding="UTF-8"?> <xmlscript> <vr id="vr_csv-Reihe" text="Wert_1,Wert_2,Wert_3,Wert_4"/> <vr id="vr_csv-Element" text=""/> <vr id="vr_doppeltes-At-Zeichen" text="\@@"/> <vr id="vr_Name_csv-Reihe" text="vr_csv-Reihe"/> <vr id="vr_csv_Position" value="0"/> <vr id="vr_csv-Element" text="@@0vr_csv-Reihe"/> <if condition="@vr_csv-Element - (null)"> <then> <while condition="@vr_csv-Element - (null)" max="100"> <trace text="@vr_csv-Element"/> <vr id="vr_csv_Position" value="#vr_csv_Position + 1"/> <vr id="vr_csv-Element" format="%s%d%s" text="@vr_doppeltes-At-Zeichen #vr_csv_Position @vr_Name_csv-Reihe" start="1"/> </while> </then> </if> </xmlscript>
Zum Abschluss noch ein Rocrail-Plan zum Download. In diesem Plan werden die Elemente eines Textfeld, in dem eine CSV-Reihe eingetragen werden kann, in einem weiteren Textfeld untereinander ausgegeben.
Hierfür wurde das obige Script leicht modifiziert.
<?xml version="1.0" encoding="UTF-8"?> <xmlscript desc="csv-verarbeitung.xml"> <!-- In diesem Script wird gezeigt, wie die Elemente einer kommaseparierten Reihe von Werten (CSV-Reihe) mit einer while-Schleife verarbeitet werden können. --> <vr id="vr_csv-Reihe" text="$tx_csv-Reihe"/> <vr id="vr_csv-Element" text=""/> <vr id="vr_doppeltes-At-Zeichen" text="\@@"/> <vr id="vr_Name_csv-Reihe" text="vr_csv-Reihe"/> <tx id="tx_csv-Einzelwerte" format="CSV-Einzelwerte:"/> <vr id="vr_csv_Position" value="0"/> <vr id="vr_csv-Element" text="@@0vr_csv-Reihe"/> <if condition="@vr_csv-Element - (null)"> <then> <while condition="@vr_csv-Element - (null)" max="100"> <tx id="tx_csv-Einzelwerte" format="| @vr_csv-Element"/> <vr id="vr_csv_Position" value="#vr_csv_Position + 1"/> <vr id="vr_csv-Element" format="%s%d%s" text="@vr_doppeltes-At-Zeichen #vr_csv_Position @vr_Name_csv-Reihe" start="1"/> </while> </then> <else> <tx id="tx_csv-Einzelwerte" format="Die CSV-Reihe|enthält keine Werte."/> </else> </if> </xmlscript>