VOICE-Homepage: http://de.os2voice.org |
Januar 2004
Inhaltsverzeichnis
|
Von Thomas Klein © Januar 2004 |
Willkommen zurück zu unserer Serie über REXX und DrDialog. Für die Unterbrechung entschuldige ich mich, aber ich brauchte eine kleine Auszeit, um die anderen Dinge zu erledigen, die sich um mich herum zu stapeln begannen. Wie dem auch sei - hier sind wir also wieder. Zu unserem letzten Teil, der von Schleifenkonstrukten handelte, gibt es übrigens noch eine Sache, die mir nachträglich eingefallen ist:
Verändern Sie niemals manuell eine Schleifen- (bzw. Zähler-) variable!
Obwohl dies in manch anderen Programmiersprachen verwendet wird, um eine Schleife vorzeitig zu
verlassen, sollten Sie in REXX von solchen "Tricks" Abstand nehmen, da dies unter
Umständen zu nicht vorhersehbaren (bzw. erwünschten) Ergebnissen führen
könnte. Verwenden Sie stattdessen die dafür eigens vorgesehene EXIT-Anweisung oder
prüfen Sie, ob eine andere Schleifenkonstruktion besser geeignet ist.
Heute widmen wir uns der Fülle an Funktionen, die REXX für das Arbeiten mit Zeichenketten ("strings") bereitstellt. Allerdings werden wir nicht die vollständige Liste abarbeiten, da einige Funktionen so spezifisch sind, daß Sie nur sehr selten damit werden arbeiten müssen. Sobald die Zahl Ihrer selbst geschriebenen Programme zunimmt, werden Sie entdecken, daß einige Funktionen ständig darin auftauchen und andere weniger. Es hängt immer von Ihrem persönlichen Ansatz ab, ein Problem zu lösen. Um eine gewisse thematische Gruppierung zu haben, ordnen wir die Funktionen in folgende Gruppen:
Falls Sie den Artikel kurz überfliegen, werden Sie sich bestimmt wundern, wo die
Anweisung PARSE
denn geblieben ist. Nun, PARSE
ist wohl einen eigenen
Artikel wert, schätze ich. Diese Anweisung alleine ist ein mächtiger Bestandteil von
REXX - sowohl in Hinblick auf Funktionalität als auch Komplexität. In einem
späteren Teil der Serie werden wir uns ausführlich mit den grundlegenden Funktionen von
PARSE
beschäftigen. Natürlich kann PARSE
noch einiges mehr,
als das, was wir dann besprechen werden, aber da diese Serie sich in erster Linie an
REXX-Anfänger richtet, halte ich es für besser, Sie nicht mit zu viel Details und
Tricks zu verwirren. Selbstverständlich kann man nahezu jeder Anforderung gerecht werden,
wenn man PARSE
bis in den "letzten Winkel" beherrscht, aber die meiste
Zeit werden Sie sich nur mit einem Bruchteil des Funktionsumfangs beschäftigen müssen
und das ist es auch, was wir an der Stelle tun werden.
Bevor man sich mit dem Inhalt von Zeichenketten ("Strings") einläßt, sollte man einige Informationen zu ihnen zusammentragen.
LENGTH
gibt Aufschluß darüber, wie viele Bytes (bzw. Zeichen) in
einem String enthalten sind. Dies beinhaltet sowohl führende als auch folgende
Leerzeichen:
/* length beispiel */
text = " Ich bin 97 jahre alt. "
say length(text)
Wenn Sie dieses Skript ausführen, wird 24 angezeigt.
VERIFY
wird verwendet um zu prüfen, ob ein String bestimmte Zeichen
enthält oder nicht. Damit das klappt, gibt man neben dem zu prüfenden String einen
weiteren an, der die "Vergleichszeichen" enthält sowie zusätzliche Optionen.
Beispielsweise können Sie damit prüfen, ob ein String eine gültige Telefonnummer
enthält - d.h., ob nur die Ziffern 0 bis 9, Leerzeichen, Bindestrich und/oder das
Pluszeichen (für internationale Schreibweise) enthalten sind. Der Vergleichsstring
würde dann wie folgt aussehen:
"0123456789 -+"
Nun kann VERIFY
sowohl dazu verwendet werden zu prüfen, ob der String nur aus
den angegebenen Vergleichszeichen besteht oder keine der Zeichen enthält. Im Prinzip gibt
VERIFY
lediglich die Position des Zeichens im zu prüfenden String zurück,
die einem bzw. keinem der Vergleichszeichen entspricht. (Je nachdem, welchen Vergleichstyp Sie
gewählt haben). Ein bißchen verwirrend, hm? Okay:
/* VERIFY beispiel */
vergleich = "0123456789 -+"
PARSE PULL tel
if VERIFY(tel, vergleich, "NOMATCH") = 0 then
say "Telefonnr. ist okay."
else
say "Telefonnr. enthält ungültige Zeichen"
Wenn im obigen Beispiel jemand +44-123 456 / 789 eingeben würde, würde
VERIFY
13 zurückgeben, da das 13. Zeichen ("/") nicht im
Vergleichsstring enthalten ist. Folglich gilt, daß VERIFY
in Verbindung mit
dem Parameter NOMATCH zurück gibt, welches Zeichen nicht im Vergleichsstring enthalten ist.
Wird daraufhin eine 0 (Null) zurückgegeben, bedeutet das, daß kein Zeichen enthalten
ist, welches nicht auch im Vergleichsstring enthalten ist... oder daß die Nummer quasi okay
ist, um es so zu sagen.
Verwendet man stattdessen den Vergleichstyp "MATCH", gibt VERIFY
die
Position des ersten Zeichen im Prüfstring zurück, welches auch im Vergleichsstring
enthalten ist. Welchen der beiden Vergleichstypen Sie verwenden, hängt davon ab, was in Ihrer
Programmlogik besser geeignet ist (bzw. was einfacher umzusetzen ist). Meistens werden Sie aber
wohl den Typ NOMATCH bevorzugen, da dieser einfacher zu lesen und seine Arbeitsweise quasi
intuitiv zu verstehen ist. Hier noch einige Hinweise zu VERIFY
- die
vollständige Syntax lautet:
ergebnis = VERIFY ( <prüfstring>, <vergleichsstring> [, "MATCH" | "NOMATCH" ] [, START] )
Vom Parameter "MATCH" bzw. "NOMATCH" braucht nur der erste Buchstabe
angegeben zu werden und es ist egal, ob dies in Groß- oder Kleinbuchstaben erfolgt.
Außerdem gibt's da noch den optionalen START-Parameter, mit dem angegeben werden kann,
ab welcher Stelle (Zeichen) der Prüfstring verglichen werden soll. Standardmäßig
beginnt der Vergleich mit dem ersten Zeichen, aber je nachdem, wie Sie Ihren Prüfstring
zusammenbasteln, könnte es von Vorteil sein, bestimmte führende Zeichen nicht in den
Vergleich einzuschließen. Als Beispiel dafür stellen Sie sich ein Adreßbuchprogramm
vor, welches intern mit Einträgen in der Form "Müller,Peter/123-4567"
arbeitet. Um nun die enthaltene Telefonnummer zu prüfen, können Sie VERIFY
mitteilen, diesen Vergleich erst ab Stelle 14 durchzuführen:
if VERIFY(tel, vergleich, "NOMATCH",14) = 0 then
Oder "abgekürzt" in der Form:
if VERIFY(tel, vergleich, "N", 14) = 0 then
Da "Nomatch" der Standardwert für den Vergleichstyp ist, können Sie ihn im obigen Beispiel auch einfach "unterschlagen". Falls Sie in solchen Fällen aber mit dem optionalen Parameter START arbeiten, müssen Sie dennoch ein zusätzliches Komma davor angeben, damit der REXX-Parser "versteht", daß der Vergleichstyp-Parameter wirklich "ausgelassen" wurde und "14" der START-Parameter ist:
if VERIFY(tel, vergleich, ,14) = 0 then
Falls Sie den kompletten String ab der ersten Stelle unter Verwendung von "Nomatch" prüfen wollen, verwenden Sie quasi alle Vorgabewerte und Sie können den ganzen Rest weglassen. Sie geben also einfach ein:
if VERIFY(tel, vergleich) = 0 then
Bezüglich des Beispiels von Herrn Peter Müller fragen Sie sich vielleicht, was Sie denn mit dem START-Parameter machen sollen, wenn ein Name verwendet wird, der länger oder kürzer ist. Eine gute Frage. Dafür benötigen wir eine zusätzliche Funktion, die wir gleich kennenlernen werden. Für's erste schauen wir uns noch die letzte Funktion der Gruppe "String-Informationen" an.
WORDS
ist eine sehr nützliche Funktion. Das Konzept der
"Wörter" in Strings, daß REXX bietet, ist verglichen mit den
Stringfunktionen in BASIC z.B. fast schon der Himmel auf Erden für Programmierer. Ein Wort
ist ein Teil eines Strings, der entweder durch Leerzeichen und/oder den Anfang bzw. das Ende des
Strings begrenzt wird. Unter den WORD-Funktionen ist WORDS
noch mit die einfachste:
Sie gibt die Anzahl der in einem String enthaltenen (erkannten) Wörter zurück:
/* words beispiel */
text = "Dies ist ein words() Beispiel. "
say words(text)
WORDS
würde in diesem Fall folgende Teilstrings erkennen:
Dies ist ein words() Beispiel.
Demnach würde WORDS
für das obige Beispiel eine 5 zurückgeben.
Um zu verdeutlichen, um was es bei WORDS
geht, lassen Sie es mich so erklären:
Wenn Sie eine Zeichenkette anschauen, dann erkennen Sie die Wörter darin, richtig? Die
Funktion WORDS()
arbeitet ziemlich genau in derselben Weise. Solange mindestens ein
Leerzeichen zwischen zwei anderen Strings steht, werden diese als zwei einzelne Wörter
erkannt. Es spielt also auch keine Rolle, ob diese Wörter durch - sagen wir mal - zwanzig
Leerstellen getrennt sind. Es sind trotzdem immer noch zwei Wörter. Eine Ausnahme ist
vielleicht, daß Sie einen einzelnen Punkt nicht als Wort erkennen würden. Bei
WORDS()
hingegen ist das der Fall, wenn dieser Punkt entsprechend durch Leerzeichen
und/oder Stringende begrenzt ist, wie z.B. in
"Es gibt 57 Programme, aber nichts vernünftiges .
"
WORDS()
würde hier 8 Wörter erkennen - beachten Sie den getrennt
stehenden Punkt.
Von allen Stringfunktionen sind dies jene, die man am meisten verwendet - zumindest gilt das
für mich. Lassen Sie uns mit einer relativ einfachen beginnen:
POS
wird verwendet, um die Stelle in einem String zu finden, an der ein
bestimmter anderer String beginnt. IBM verwendet in der REXX-Hilfe das "Nadel und
Heuhaufen"-Prinzip - das ist eine ziemlich gute Methode, um sich die Syntax zu merken:
ergebnis = POS( <nadel>, <heuhaufen> [, START])
POS
sucht im <heuhaufen>-String nach der ersten Stelle, die <nadel> enthält.
Entweder wird dann die Startposition von <nadel> innerhalb <heuhaufen>
zurückgegeben (wobei 1 der erste Buchstabe ist usw.) oder eine 0 (null), wenn <nadel>
in <heuhaufen> nicht gefunden wurde. Optional können Sie POS
auch eine
Position mitgeben, ab der <heuhaufen> durchsucht werden soll - standardmäßig ist
das 1, also die erste Stelle bzw. der erste Buchstabe. Das ist zum Beispiel dann nützlich,
wenn Sie bestimmte Teilstrings aufstöbern möchten - obwohl die WORD-Funktionen, die wir
gleich besprechen, hierbei einen erheblich besseren Dienst verrichten.
Stellen Sie sich beispielsweise einmal vor, daß Sie einen String namens
"datensatz" haben, der folgendes enthält:
"vorname=Peter, nachname=Müller, tel=123-45678"
Wenn Sie daraus jetzt die Telefonnummer benötigen (und wir davon ausgehen, daß es nur eine gibt und diese immer am Ende von 'Datensatz' steht), würden Sie mit folgender Anweisung
telbeginn = POS("tel=", datensatz)
die Startposition des gesuchten Teilstrings innerhalb 'datensatz' erhalten. Oder eine
NULL, falls keine Angabe enthalten ist. Wenn Sie also irgendetwas anderes als eine Null
zurückbekommen, addieren Sie 4 (die Länger von "tel=") zu diesem Wert und Sie
wissen, an welcher Stelle innerhalb von 'datensatz' die Telefonnummer beginnt. Dann
bräuchten Sie nur noch die Länge der Telefonnummer zu bestimmen und schon könnten
Sie sich diese in einem anderen String übertragen - allerdings benötigen Sie
hierfür noch eine andere Funktion ("substr"), die wir erst weiter unten
behandeln.
Persönlich verwende ich diese Funktion meist nur zur Prüfung, ob ein bestimmter String
in einem anderen überhaupt enthalten ist - egal wo er denn steht, wie z.B.
in:
IF POS("/?", parameterstring) \= 0 then call DisplayHelp
...was soviel bedeutet wie "wenn 'parameterstring' ein '/?' enthält, rufe die Funktion DisplayHelp auf (die dann wohl einen klugen Text ausgibt)".
LASTPOS
macht so ziemlich dasselbe, allerdings sucht man hiermit den
<heuhaufen> von hinten nach vorne durch - also rückwärts. Diese Funktion
verwendet dieselben Parameter (START) und gibt auch dieselbe Information zurück.
LASTPOS
ist der komfortable Weg, die letzte Stelle im <heuhaufen> zu finden,
an der eine <nadel> herumliegt. Natürlich könnte man hierzu auch eine variable
Abfolge von POS
-Aufrufen verwenden, deren START-Parameter jeweils auf die vorher
gefundene Stelle angepaßt wird, aber hey: Wozu unnötig komplizieren?
Persönlich verwende ich LASTPOS
meist dann, wenn ich es mit voll-qualifizierten
Dateinamen zu tun habe (also Dateinamen, die Laufwerk- und volle Pfadangabe enthalten). Sobald
ich die Position des letzten backslash ("\") kenne, weiß ich, daß alles
"dahinter" der Dateiname sein muß und alles "davor" entsprechend die
Laufwerks- und Pfadangabe. Ja - ich könnte natürlich auch die Funktion
FILESPEC()
verwenden, aber je nach Programmsituation benötigt man manchmal eben
andere Informationen wie bspw. den Namen der letzten Verzeichnisebene...
Die "WORD-Funktionen" (word, wordpos, wordindex, wordlength
und
subword
) sind extrem nützlich, wenn man sich mit Strings herumschlagen muß,
die aus mehreren, durch ein oder mehrere Leerzeichen voneinander getrennten Teilstrings bestehen.
Wenn Sie jemals versucht haben, solche Strings "von Hand" zu zerlegen, z.B. in einem
älteren BASIC-Dialekt, dann werden Sie nach der Lektüre der folgenden Absätze wohl
zustimmen, daß man sich mit REXX' Funktionsumfang fast schon wie im
"Programmiererparadies" fühlt. ;)
Als Beispiel für alle folgenden Abschnitte (soweit nicht anders vermerkt), nehmen wir an,
daß wir einen String namens "eingabe" vorliegen haben, der mehrere Teilstrings
(bzw. "Wörter") hat, die durch eine unbekannte Anzahl von Leerstellen getrennt
sind... also um es kurz zu machen: "Mary hat 5 kleine
Lämmer."
WORDS
(wie wir bereits wissen) gibt uns die Anzahl der "Teile"
(also "Wörter") zurück, die im String enthalten sind.
SAY WORDS(eingabe)
würde ausgeben: 5
WORD
wird verwendet, um ein einzelnes Wort aus dem String zu ermitteln, wobei es
gleichzeitig noch von führenden und folgenden Leerstellen "befreit" wird. Damit
das funktioniert, müssen Sie WORD
natürlich auch sagen, welches Wort
ermittelt werden soll, indem Sie einfach die "Nummer" des Worts innerhalb des Strings
angeben (1 für das erste, 2 für das zweite usw.). Somit würde also
SAY WORD(eingabe, 2)
folgendes ausgeben: hat
WORDPOS
funktioniert ähnlich wie das weiter oben beschriebene
POS
- mit der Ausnahme, daß es nicht zeichenorientiert arbeitet, sondern eben
auf Basis von Wörtern: Der <heuhaufen> wird nach der ersten (von links nach rechts)
auftretenden <nadel> durchsucht. Als Rückgabewert erhält man entsprechend die
"Nummer" des Wortes in <heuhaufen>, das der <nadel> entspricht. Genau wie
bei POS
, kann auch WORDPOS
mittels eines optionalen Parameters dazu
bewegt werden, den <heuhaufen> nicht vom Anfang, sondern erst ab einer bestimmten Stelle zu
durchsuchen. Auch hierbei handelt es sich wiederum um eine "Wortnummer". Die
vollständige Syntax lautet also:
ergebnis = WORDPOS(<nadel>, <heuhaufen> [ ,START ] )
Beachten Sie, daß <nadel> mit dem zu suchenden Wort in <heuhaufen> exakt übereinstimmen muß - das bedeutet, auch die Groß-/Kleinschreibung muß identisch sein.
SAY WORDPOS("HAT", eingabe)
würde daher nur 0
ausgeben, da "HAT"
nicht identisch mit "hat" ist... wohingegen
SAY WORDPOS("hat", eingabe)
ausgeben würde: 2
Weiter ist auch zu beachten, daß mehr als nur ein Wort als <nadel> verwendet werden
kann. In diesem Fall behandelt WORDPOS
den Inhalt von <nadel> genau wie alle
WORD-Funktionen den <heuhaufen> behandeln: Die Inhalte werden intern in einzelnen
Wörtern abgelegt. Beispiel:
SAY WORDPOS("hat 5", eingabe)
gibt aus: 2
Dies funktioniert also, obwohl 'eingabe' tatsächlich "Mary hat
5 kleine Lämmer." enthält (wobei 4 Leerstellen zwischen
'hat' und '5' stehen), unsere <nadel> jedoch nur 1 Leerstelle enthält.
Da jedoch <nadel> und <heuhaufen> jeweils als Folge von voneinander getrennten
Wörtern behandelt werden, funktioniert der Vergleich.
WORDINDEX
ermittelt die Anfangsposition eines bestimmten Wortes innerhalb des
kompletten Strings - mit eingerechnet aller evtl. davor vorhandenen Leerstellen.
SAY WORDINDEX(eingabe, 2)
würde demnach ausgeben: 7
Die SUBSTR
Funktion ermittelt einen Teilstring eines Strings auf Basis von
Zeichenzahl und Länge:
SAY SUBSTR(eingabe, 3, 17)
gibt beispielsweise den Teil von 'eingabe' zurück, der mit dem dritten Zeichen
beginnt und 17 Zeichen lang ist. Das wäre alsory hat
5 kle
Falls Sie mit der MID$-Funktion von BASIC vertraut sind, beachten Sie, daß im Gegensatz
dazu SUBSTR
nicht zum Verändern eines Strings verwendet werden kann, sondern
lediglich zur Abfrage. Optional kann SUBSTR
auch dazu bewegt werden, "nicht
existierende" Teile des Teilstrings mit einem angegebenen Zeichen zu füllen.
"Nicht existierende Teile" sind in diesem Fall solche, die außerhalb des
eigentlichen Strings liegen. Beispiel? Wenn Ihr Programm die Zeichen 3, 4 und 5 eines Strings
auswertet, und dieser String zufälligerweise gerade nur einmal 3 Zeichen lang ist erhalten
Sie keine Fehlermeldung! Statt dessen erhalten Sie Zeichen 3 und zwei Leerstellen - denn der
Vorgabewert (falls Sie kein Füllzeichen angeben) ist das Leerzeichen. Wenn Sie also ein
Füllzeichen angeben würden, erhielten Sie Zeichen 3 und zwei Füllzeichen
zurück:
SAY SUBSTR(eingabe, 25, 10)
würde ausgeben: "ämmer.
"
(ohne die Anführungszeichen - die habe ich nur hinzugefügt, damit man die
angehängten Leerzeichen erkennt.)
SAY SUBSTR(eingabe, 25, 10, "-")
würde ausgeben: "ämmer.----"
Eine nette Eigenschaft von SUBSTR
ist übrigens, daß, wenn man keinen
Längenparameter angegeben hat, der ganze "Rest" ab der angegebenen Stelle
ermittelt wird:
SAY SUBSTR(eingabe, 17)
gibt demnach aus: kleine Lämmer.
Die beiden Stringfunktionen, die ich selbst in fast jedem Programm irgendwie verwende sind
left
und right
. Hiermit wird ein Teilstring in der angegebenen
Länge ermittelt, wobei entweder vom rechten oder linken Ende des Ausgangsstrings
begonnen wird - je nachdem, welche der beiden Funktionen man eben verwendet:
SAY LEFT(eingabe, 7)
würde "Mary h"
ausgeben
SAY RIGHT(eingabe, 7)
entsprechend "Lämmer."
ausgeben
würde (beide Male jeweils ohne die Anführungszeichen natürlich).
Genau wie bei SUBSTR
, können nicht vorhandene Bereiche mit left
und right
aufgefüllt werden (also solche, die außerhalb des zu
verarbeitenden Strings liegen). Hierfür werden ebenfalls standardmäßig
Leerzeichen verwendet, wenn kein anderes explizites Füllzeichen angegeben wird wie
beispielsweise in
SAY LEFT("abcdefg",10,"-")
Das würde dann nämlich resultieren in der Ausgabe von abcdefg---
SUBWORD
arbeitet ähnlich wie SUBSTR
, da auch hier mit
Startposition und Länge gearbeitet wird. Abgesehen von der Tatsache, daß hier nun
wieder auf Basis von Wörtern statt Zeichen ermittelt wird, gibt es noch ein paar weitere
Unterschiede: Hier findet kein Auffüllen von nicht vorhandenen Bereichen statt wie in
SUBSTR
. Wir erinnern uns, daß unser String 'eingabe' fünf
Wörter enthält. Wenn wir nun die Wörter 4, 5 und 6 aus 'eingabe' ermitteln
wollen, indem wir codieren
SAY SUBWORD(eingabe, 4, 3)
würde uns die Funktion schlicht "kleine
Lämmer."
ausgeben (natürlich wieder ohne die Anführungszeichen - die habe diesmal eingebaut,
damit man erkennt, daß keine Füllzeichen vorhanden sind).
Ein weiterer wichtiger Punkt hierbei ist, daß SUBWORD
die zwischen den zu
ermittelnden Wörtern enthaltenen Leerzeichen genau so zurück gibt, wie sie im
Ausgangsstring enthalten sind - das bedeutet also, daß hier intern keine Aufteilung in
einzelne Wörter passiert:
SAY SUBWORD(eingabe, 1, 3)
würde demnach ausgeben: "Mary hat
5"
Allerdings wird wie in SUBSTR
der ganze "Rest" des Ausgangsstrings ab der
angegebenen Startposition (Startwort) zurückgegeben, wenn man den Längenparameter
einfach weg läßt.
WORDLENGTH
schließlich ermittelt, aus wie vielen Zeichen ein
angegebenes Wort besteht:
SAY WORDLENGTH(eingabe, 3)
würde also (für das Wort "5") ausgeben: 1
Abgesehen davon, daß man neue Strings dadurch erzeugt, daß man Teile vorhandener Strings mittels bestimmter Funktionen ermittelt, gibt es noch andere Wege, das zu erreichen. Was übrigens das "Verändern" von Strings betrifft, muß ich gestehen, daß wir so etwas in Wirklichkeit gar nicht machen, sondern vielmehr auch hier neue Strings aus anderen erstellt werden. Manchmal sieht es so aus, als ob das anders wäre, wenn wir beispielsweise das Ergebnis sofort in die Ausgangsvariable zurücklegen, wie in
meintext = left(meintext, 5)
aber im Prinzip "ändern" wir den String nicht wirklich: Es wird "gleichzeitig" intern ein neuer String erzeugt und sofort wieder zurück übertragen. Aber das ist jetzt nicht so wichtig - machen wir lieber weiter mit dem Artikel.
COPIES
erzeugt einen String, indem eine Verkettung mehrerer Kopien eines
anderen Strings erfolgt:
SAY COPIES("bla", 3)
würde beispielsweise ergeben: blablabla
Na super. COPIES
ist meistens dann recht praktisch, wenn man im VIO-Modus mit
schönen Trennzeilen arbeiten will, die eine bestimmte Länge haben sollen:
SAY COPIES("-", 18)
ergibt dann ------------------
XRANGE
ist extrem nützlich, wenn man mit Zeichenumsetzung arbeitet.
Wie Sie wahrscheinlich wissen, ist jedes Zeichen Bestandteil einer "Zeichentabelle" und
wird darin durch einen Index (eine Nummer) identifiziert. Wir nennen das 'mal die
"ASCII-Tabelle". XRANGE
macht sich diese Tatsache zunutze und erzeugt
einen String, der aus einer Abfolge von Zeichen besteht, die sich an der Reihenfolge der
Zeichentabelle orientiert und durch Start- und Endzeichen angegeben wird:
kleinesalphabet = XRANGE("a", "z")
SAY
kleinesalphabet
gibt beispielsweise aus: abcdefghijklmnopqrstuvwxyz
Beachten Sie, daß die ASCII-Tabelle aus 256 Zeichen besteht (mit den Nummern 0 bis 255).
Wenn Sie jetzt die komplette Tabelle anzeigen wollen, müssen Sie die Zeichen in
Hex-Schreibweise angeben, da es sich bei Zeichen Nummer 0 und Zeichen Nummer 255 um
nicht-druckbare Zeichen (und somit auch nicht-eingebbare) handelt:
SAY XRANGE("00"x, "FF"x)
zeigt Ihnen den kompletten ASCII-Zeichensatz an (sofern die jeweiligen Zeichen am Bildschirm
darstellbar sind...).
Beachten Sie bitte auch, daß, wenn das Endzeichen in der ASCII-Tabelle "vor" dem
angegebenen Startzeichen steht, XRANGE
eine kurze Runde dreht: Zunächst werden
die Zeichen beginnend mit dem Startwert ausgegeben bis zum Zeichen Nummer 255, dann wird bei
Zeichen 0 fortgesetzt und alle Zeichen bis zum Endzeichen werden ausgegeben. Sie bekommen also
nicht etwa die "umgekehrte" Zeichenfolge, sondern vielmehr etwas, das Sie nicht
unbedingt so erwartet hätten...;)
STRIP
kennen wir schon aus anderen Beispielen: Hiermit werden
führende und folgende Zeichen aus einem String entfernt. Oder vielmehr, wie ich weiter oben
bereits erklärt habe, wird ein neuer String erzeugt, der diese Zeichen nicht mehr
enthält. Standardmäßig (wenn nichts anderes angegeben) werden hiermit Leerzeichen
entfernt, man kann aber auch andere Zeichen angeben. Außerdem kann auch angegeben werden,
ob nur führende, nur folgende oder beide Typen entfernt werden sollen. Hierfür ist der
Standardwert "Beide".
SAY STRIP(" Mary. ")
würde ausgeben Mary.
Dies passiert genau genommen deshalb, weil keine optionalen Parameter angegeben wurden und somit
die Vorgabewerte "entferne führende und folgende Leerzeichen) zur Geltung
kommt.
SAY STRIP("0012.850", "L", "0")
würde ergeben (anzeigen) 12.850
während
SAY STRIP("0012.850", , "0")
12.85
ausgeben würde.
Beachten Sie im obigen Beispiel, daß auch hier wieder das Komma explizit angegeben werden
muß, damit REXX' Parser versteht, daß es sich bei "0" um das zu entfernende
Zeichen handelt und der Typ-Parameter "unterschlagen" wurde. Würde man
stattdessen
SAY STRIP("0012.850", "0")
schreiben, erhielte man eine Fehlermeldung, denn "0" würde dann (zu Recht) als Typ-Parameter (führend/folgend) erkannt, was leider nicht erlaubt ist da nur "L", "T" und "B" erlaubt sind. Eigentlich lauten die Werte für den Typ-Parameter "Leading" (führend), "Trailing" (folgend) bzw. "Both" (beide), aber wer hat schon die Zeit, alle diese Buchstaben zu tippen, wenn es auch genügt, jeweils den ersten anzugeben? ;)
INSERT
sieht bei erster Betrachtung ziemlich komplex aus. Die
vollständige Syntax lautet
ergebnis = INSERT ( <eingabe> , <text> [, START ] [, LÄNGE ] [,
FÜLLZEICHEN ] )
Im Prinzip wird damit ein angegebener Text in einen anderen eingefügt unter Angabe der Einfügeposition, also genau so, als wenn Sie in einem Texteditor den Cursor an eine bestimmte Stelle setzen und anfangen würden, Text einzugeben:
SAY INSERT("123", "abcde", 3)
würde beispielsweise ausgeben: abc123de
Beachten Sie, daß der Vorgabewert für START 0 (null) ist, d.h. der einzufügende
Text würde dann vor den bestehenden Zieltext geschrieben:
SAY INSERT("123, "abcde")
ergibt 123abcde
Solange die Standardwerte für Sie ausreichen, müssen Sie also nichts weiter beachten. Wenn
Sie allerdings etwas mehr Funktionalität brauchen, müssen Sie verstehen, wie sich die
optionalen Parameter LÄNGE und FÜLLZEICHEN auf das Verhalten der Funktion auswirken...
also: Mit LÄNGE geben Sie den Platz an, den der einzufügende Text <eingabe> im
Zieltext <text> belegen soll. Standardmäßig wird zum Auffüllen des
nicht-belegten Bereichs dann das Leerzeichen verwendet...
SAY INSERT("123", "abcde", 3, 5)
würde also ergeben abc123 de
Wenn Sie ein (abweichendes) Füllzeichen angeben, wird dieses zum Auffüllen der durch
LÄNGE angegebenen Zeichen verwendet, die von <eingabe> nicht belegt werden, wie
in:
SAY INSERT(123, "abcde", 3, 5, "#")
Dies ergibt dann entsprechend: abc123##de
DELSTR
entfernt einen Teilstring aus einem anderen String. Im Prinzip
arbeitet diese Funktion genau wie ihr Texteditor, wenn Sie den Cursor an eine bestimmte Position
stellen, eine Anzahl von Zeichen markieren und dann die Entf-Taste drücken würden. Es
werden dieselben Parameter verwendet wir in SUBSTR
- nämlich Startposition und
Länge (in Zeichen):
SAY DELSTR("abcde", 3, 2)
würde ergeben: abe
Und genau wie bei
SUBSTR
, bedeutet auch hier ein fehlender Längenparameter, daß "der
ganze Rest" ab der angegebenen Position gelöscht werden soll:
SAY DELSTR("abcde", 3)
bewirkt: ab
DELWORD
ist das entsprechende Gegenstück zu DELSTR
,
wenn's um die WORD-Funktionen geht. Wir wenden uns wieder Mary und ihren Lämmern zu,
damit Sie uns hilft, das Verfahren zu verdeutlichen:
SAY DELWORD(eingabe, 2, 2)
würde zwei Wörter aus 'eingabe' entfernen, beginnend mit dem zweiten Wort...
übrig bliebe dann (die Ausgabe): Mary kleine
Lämmer.
DELWORD
führt kein internes "Parsen" aus- das bedeutet, daß die
Leerstellen zwischen dem zu entfernenden Bereich und dem Rest nicht vollständig
aufgelöst werden. Vielmehr werden nur der zu löschende Bereich inklusive der ihm
folgenden Leerstellen beachtet. Um es zu verdeutlichen:
SAY DELWORD("abc def ghi jkl", 2, 2)
ergäbe: abc jkl
Warum ist eine Lücke von 3 Leerzeichen zwischen den beiden verbliebenen Wörtern? Das
geschieht wir folgt: "def ghi" sind die zwei zu löschenden
Wörter. Zuzüglich der drei Leerstellen hinter "ghi". Also wird
"def ghi " aus dem Text ausgeschnitten. Übrig bleiben
"abc " vor und "jkl" dahinter. Klebt man die zusammen, ergibt das
"abc jkl".
CENTER
ist quasi eine spezielle Variante von INSERT
. Hiermit
wird ein String innerhalb eines neu zu erstellenden Strings zentriert. Die verbleibenden
"Ränder" links und rechts können mit einem angegebenen optionalen Zeichen
gefüllt werden. Damit lassen sich im VIO-Modus beispielsweise prima Überschriften
erzeugen:
SAY CENTER("Mary's Lämmer", "20", "-")
will display ---Mary's Lämmer----
Lustige Sache. Hier 'mal ein kleines Programm für einen möglichen Verwendungszweck
von COPIES
und CENTER
:
/* das mary beispiel */
lambname.0 = 5
lambname.1 = "itchy"
lambname.2 = "sparky"
lambname.3 = "joey"
lambname.4 = "samantha"
lambname.5 = "lou"
say center("Mary's Lämmerliste", 30, "-")
do i = 1 to lambname.0
say center(lambname.i, 30)
end
say copies("-", 30)
Dieses unglaubliche, kleine Programm gibt dann folgendes aus:
------Mary's Lämmerliste------
itchy
sparky
joey
samantha
lou
------------------------------
Super, oder? ;)
Sie fragen sich wahrscheinlich, was denn passiert, wenn man einen String in einem Bereich
zentrieren will, der kleiner ist, als der String selbst, nicht wahr? Aber kein Sorge -
nööö, es gibt keine Fehlermeldung, sondern das ganze wird einfach passend
zurechtgestutzt:
SAY CENTER("Das ist nicht lustig!", 7)
gibt " nicht "
aus.
Wenn eine gerade Anzahl von Zeichen auf eine ungerade Anzahl trifft (und eine exakte Zentrierung
mit dem gleichen Abstand links und rechts nicht möglich ist), erfolgt eine Anpassung
(hinzufügen oder entfernen eines Füllzeichens) grundsätzlich auf der rechten
Seite.
Interessant ist übrigens, daß man diese Funktion sowohl als
"center
" als auch als "centre
" aufrufen kann. Mann,
das nenne ich "IBM-Qualität". ;)
REVERSE
ist nicht wirklich abstrakt: Hiermit erhält man die
umgekehrte Zeichenfolge zum übergebenen String. Wenn Sie also jemals wissen wollten, wie Ihr
Vorname rückwärts geschrieben aussieht, versuchen Sie's einfach mal:
SAY REVERSE("thomas")
Natürlich müssen Sie 'thomas' durch Ihren Vornamen ersetzen, damit das
Programm korrekt arbeitet. ;) Außer natürlich, Sie heißen tatsächlich auch
Thomas.
ist eine geniale Funktion für das Arbeiten mit "Wörtern".
Hiermit können Sie erreichen, daß alle Wörter eines Strings automatisch mit einer
identischen Anzahl Zeichen voneinander getrennt werden. Haben Sie schon einmal versucht,
"manuell" die Wörter eines Strings zu ermitteln, um Sie danach mit jeweils einem
Leerzeichen voneinander zu trennen? Das schaffen Sie in REXX mit einer einzigen
Anweisung:
SPACE
SAY SPACE(eingabe)
und schon erscheint: Mary hat 5 kleine
Lämmer.
Aber natürlich verschafft uns das Verwenden der Vorgabewerte hier einen Vorteil, denn
eigentlich müßte es ausformuliert heißen
SAY SPACE(eingabe, 1, " ")
damit klar ist, daß wir die Wörter durch jeweils ein (1) Leerzeichen (" ") trennen wollen. Aber warum nicht 'mal durch jeweils 2 Unterstriche voneinander trennen?
SAY SPACE(eingabe, 2, "_")
würde anzeigen: Mary__hat__5__kleine__Lämmer.
Bestimmt haben Sie schon gemerkt, daß also auch SPACE
intern den String in
einzelne Wörter zerlegt, um zum Ziel zu kommen. Diese Funktion eignet sich hervorragend zum
"Normalisieren" von Benutzereingaben oder den Ausgaben anderer Programme, wenn Sie
diese in einer bestimmten Form vorverarbeiten müssen. Beachten Sie auch, daß die
Angabe von 0 (null) als Abstand dazu führt, daß alle angegebenen Trennzeichen entfernt
werden:
SAY SPACE(eingabe, 0)
würde also anzeigen: Maryhat5kleineLämmer.
Ich muß zugeben, daß ich mich bis heute nie mit OVERLAY
beschäftigt habe. Nachdem ich mir die Funktion aber jetzt im Rahmen des Artikels näher
angeschaut habe (und scheinbar endlich verstehe, wofür sie gut ist) werde ich sie
wahrscheinlich in Zukunft öfter verwenden.
Was OVERLAY
eigentlich macht, ist nichts anderes als wie INSERT
einen
Text in einen anderen einzufügen (es werden sogar die identischen Parameter und Syntaxschemata
verwendet) mit der Ausnahme, daß - um es so zu sagen - nicht der
"Einfügemodus" sondern der "Überschreibmodus" beim Einfügen
verwendet wird, falls Sie verstehen was ich meine. (Hätte man diese Funktion
"OVERWRITE" genannt, hätte ich's auch sofort kapiert.):
SAY OVERLAY("=XYZ=", "01234567890", 4)
ergibt nämlich 012=XYZ=890
Wenn Sie die optionalen Parameter verwenden möchten, schauen wir uns erst noch 'mal das
Syntaxdiagramm an:
ergebnis = OVERLAY ( <eingabe> , <text> [, START ] [, LÄNGE ] [, FÜLLZEICHEN ] )
Wenn Sie einen Wert für LÄNGE angeben, wird <eingabe> mit dem angegebenen <füllzeichen> auf die angegebene Länge aufgefüllt. Das vorgegebene Füllzeichen ist natürlich mal wieder das Leerzeichen, daher wird mit dem Befehl
SAY OVERLAY("=XYZ=", "01234567890", 4, 6)
folgendes angeziegt: 012=XYZ= 90
Der Vorgabewert für START ist 1, was also bedeutet, daß <text> ab dem ersten
Zeichen durch <eingabe> überschrieben wird.
Zu guter Letzt hätten wir noch TRANSLATE
, die auch eine
"coole" Funktion für das Arbeiten mit Strings darstellt. Hiermit können Sie
zwei Zeichentabellen definieren, die zum Austauschen der Zeichen eines Strings verwendet werden.
Jedes Zeichen im String (das in der "Eingabetabelle" enthalten ist) wird durch das
passende Gegenstück (in der "Ausgabetabelle") ersetzt. Beide Tabellen werden
jeweils einfach in Form eines Strings angegeben. Die passenden "Gegenstücke"
werden dabei durch die Position des Zeichens innerhalb der Tabellen ermittelt (Zeichen 1 in
"Eingabetabelle" entspricht Zeichen 1 der "Ausgabetabelle" usw.).
Das Syntaxdiagram sieht wie folgt aus:
ausgabe = TRANSLATE( <eingabe> [, <ausgabe-tabelle>] [,
<eingabe-tabelle>] [, FÜLLZEICHEN] )
Das FÜLLZEICHEN wird dazu verwendet, die <ausgabe-tabelle> auf die gleiche Länge wie die <eingabe-tabelle> zu bringen, falls diese größer sein sollte. Damit wird erreicht, daß für jede Position der <eingabe-tabelle> ein entsprechendes "Gegenstück" in der <ausgabe-tabelle> vorhanden ist. So weit so gut. Da es sich bei allen Parametern "hinter" <eingabe> um optionale Bestandteile handelt, werden Sie sich vielleicht fragen, was denn aus <eingabe> wird, wenn nichts weiter angegeben wird... ganz einfach: <eingabe> wird in Großbuchstaben umgewandelt und das war's:
SAY TRANSLATE("hallo")
ergibt folglich: HALLO
Die in <eingabe> enthaltenen Zeichen, für die es keine Übereinstimmung in der
<eingabe-tabelle> gibt, werden unverändert nach <ausgabe> übernommen. Damit
lassen sich prima unerwünschte Zeichen entfernen: Ersetzen Sie alle unerwünschten
Zeichen durch Leerzeichen und verwenden Sie danach die Funktion SPACE
.
Beispielsweise könnten Sie so alle Vokale aus einem String entfernen::
ausgabe = TRANSLATE("hallo", copies(" ", 5), "aeiou")
Damit wird eine <eingabe-tabelle> erstellt, die alle Vokale enthält sowie eine <ausgabe-tabelle> von passender Länge, die also 5 Leerzeichen enthält. Somit wird also jeder Vokal durch ein Leerzeichen ersetzt. Dies ergäbe als Ausgabe "h ll ". Dann verwenden Sie noch die SPACE-Funktion mit einem Abstandswert von 0:
SAY SPACE(ausgabe, 0)
und schon erscheint hll
Oder hier das Ganze als Einzeiler:
SAY SPACE(TRANSLATE("hallo", copies(" ", 5), "aeiou"), 0)
Okay, das ist vielleicht kein perfektes Beispiel. Aber stellen Sie sich vor, daß Sie ein
mehrzeiliges Texteingabefeld haben und nun ermitteln möchten, wie viele Zeilen darin
enthalten sind. Jetzt brauchen Sie nur alles außer "0D"x (was soviel bedeutet wie
"Zeilenvorschub" bzw. "line feed" / "LF") durch Leerzeichen zu
ersetzen, diese mit SPACE()
loszuwerden und dann die Länge des verbleibenden
Texts zu prüfen. Fertig. Diese Methode funktioniert natürlich auch für
Textdateien, nachdem deren Inhalt in eine Variable gelesen wurde. Als ich das zum ersten mal
selber getestet habe, konnte ich nicht glauben, wie viel schneller diese Methode ist, als das,
was ich bis dahin selbst zusammenprogrammiert hatte. Mal probieren? Passen Sie aber auf,
daß Ihre Textdateien nicht zu groß sind (ich habe es nur mit Dateien bis zu 52KB
getestet).
/* zeilenzähler beispiel */
fname="c:\temp\testfile.txt" /* tragen sie hier ihre datei ein */
tablein = xrange("00"x, "FF"x) /* komplette ascii-tabelle, 256 bytes */
tableout = copies(" ", 13) || "0D"x || copies(" ", 242) /* 13 leerz. + LF + 242 leerz. = 256 bytes) */
fchars=charin(fname, 1, chars(fname)) /* kompletten dateiinhalt in variable lesen */
result = TRANSLATE(fchars, tableout, tablein) /* alles löschen außer LF-Zeichen */
say length(space(result, 0)) + 1
Ein anderes einfaches Beispiel für die Verwendung von TRANSLATE
und
REVERSE
ist diese simple "Verschlüsselung":
/* verschlüsselungs beispiel */
/* achtung: dies ist eine NICHT sichere (weil ziemlich dumme) verschlüsselungsmethode! */
intab = xrange("a", "z")
outab = reverse(intab)
enc = translate("thomas", outab, intab)
say enc
dec = translate(enc, intab, outab)
say dec
Was hier passiert, ist das zunächst eine <eingabe-tabelle> erstellt wird, die das
alphabet in Kleinbuchstaben enthält. Dann wird die <ausgabe-tabelle> erstellt, indem
die umgekehrte Zeichenfolge (reverse) der <eingabe-tabelle> verwendet wird. Das
Verschlüsseln geschieht mittels 'translate' und den beiden Tabellen.
Das "Entschlüsseln" geschieht mittels eines erneuten translate, wobei
<eingabe-tabelle> und <ausgabe-tabelle> aus der Verschlüsselung gegeneinander
getauscht werden.
Viel Spaß damit.
Im nächsten Teil werden wir uns kurz mit den gängigsten, interessantesten bzw. nützlichsten "Hilfs-" Funktionen von REXX beschäftigen. Vielen Dank für Ihre Geduld und bleiben Sie dran! ;)
Artikelverzeichnis
editor@os2voice.org
< Vorherige Seite | Inhaltsverzeichnis | Nächste Seite >
VOICE-Homepage: http://de.os2voice.org