Zum Hauptinhalt springen

Funktionen auf Zeichenketten

Einführung

Zeichenketten (Text, String) können als Teil eines Geräteprotokolls auftreten, z.B. als Fehlermeldung eines Subsystems oder der Messdatenübertragung über eine serielle Schnittstelle. Auch viele WEB-Basierte Kommuniationsprotokolle bauen auf Strings auf.

Das Mathemodul unterstützt den skalaren Typ string, in dem typischerweise mit UTF-8 kodierte Zeichen enthalten sind.

Alle Datentypen können z.B. mit str() in den string-Typ gewandelt werden oder mit den anderen cast-Operatoren in die entsprechenden Zieltypen, sofern dabei keine syntaktischen Fehler auftreten.

Operatoren auf Text

Mit dem Datentyp String werden folgende Operatoren unterstützt:

  • alle Vergleichsoperatoren, dabei gilt die numerische Reihenfolge der ASCII-Zeichen: z.B. '4' < 'A' < 'a'

  • + - Operator, verknüpft zwei Strings

    s_connected = s1 + s2;
    tipp

    str(x) wandelt jeden Datentyp in eine String-Darstellung, auch Mehrdimensionale Objekte wie Vektoren und Matrizen. Um mehr Kontrolle über die Formatierung zu bekommen, kann die Funktion sFormat(f, ...) hilfreich sein.

Eigenschaften und Basisfunktionen

Länge einer Zeichenkette "sLength"

Die Länge einer Zeichenkette s in Bytes wird wie folgt berechnet

l = sLength(s);

In der UTF-8 Kodierung können mit entsprechender Kodierung Sonderzeichen auch mehrere Bytes belegen. Diese Funktion bestimmt die Anzahl der Bytes, nicht die Anzahl der (druckbaren) Zeichen!

Extraktion einer Teilzeichenkette als Kopie "sCopy"

Ein Teilstring r des Strings s kann wie folgt extrahiert werden

r = sCopy(s, b); // Kopie der Zeichenkette s ab Position b
r = sCopy(s, b, c); // Kopie der Zeichenkette s ab Position b der Länge c
r = sCopy(s, BC); // BC ist ein Vektor der Länge 2, der b und c beinhaltet (Verhalten wie bei sCopy(s, b, c)
r = sCopy(s, BC, sel); // BC ist eine Matrix mit zwei Spalten, die zu b und c korrespondieren,
// wobei sel einen selektierenden Zeilenindex darstellt

Die Matrix BC ist an vielen Stellen das Ergebnis von Suchfunktionen, z.B. sFind(), die mehrere Treffer in dem String liefern können. Über den Index sel kann der entsprechende Textteil unmittelbar extrahiert werden.

Beispiel:

// 0 1 2 3
// 0123456789012345678901234567890123456789
geo = 'lat: 51.234567, lon: 12.3456789';
p6 = sFind(geo, 'lat:\s*([0-9.+-]+)', {subex: true});
// => p6 := [ [ 0,14] // refers to 'lat: 51.234567'
// , [ 5, 9]]; // refers to '51.234567'
lat = dbl(sCopy(geo, p6, 1));
// => lat := 51.234567;

Extraktion des Stringanfangs und -endes "sLeft", "sRight"

Stringanfang b und Stringende e der Länge l des Strings s können wie folgt als Kopie erhalten werden

b = sLeft(s,l);
e = sRight(s,l);

Entfernen eines Teilstrings "sErase"

Um eine Kopie eines Strings s zu erhalten, in der ein Teilstring entfernt wurde, kann folgendes verwendet werden

r = sErase(s, b); // Kopie der Zeichenkette s ohne Zeichen ab Position b
r = sErase(s, b, c); // Kopie der Zeichenkette s ohne Zeichen ab Position b der Länge c
r = sErase(s, BC); // BC ist ein Vektor der Länge 2, der b und c beinhaltet (Verhalten wie bei sErase(s, b, c)
r = sErase(s, BC, sel); // BC ist eine Matrix mit zwei Spalten, die zu b und c korrespondieren,
// wobei sel einen selektierenden Zeilenindex darstellt

Die Matrix BC ist an vielen Stellen das Ergebnis von Suchfunktionen, z.B. sFind(), die mehrere Treffer in dem String liefern können. Über den Index sel kann der entsprechende Textteil unmittelbar entfernt werden.

Einfügen eines Strings "sInsert"

Um einen String t innerhalb eines Strings s an Position b einzufügen und eine Kopie r zurückzuliefern, kann folgendes verwendet werden

r = sInsert(s, b, t);

Erzeugen, Formatieren, Lesen, Aufräumen

Extraktion von Werten aus einem String "sScan" 1

Diese Funktion extrahiert Werte aus einem String s anhand eines Formatstrings f:

v = sScan(f, s);

Der Formatstring f enthält Platzhalter der Form %<x> (der Buchstabe nach % ist beliebig und hat keinen Einfluss auf den Ergebnistyp) sowie Literal-Teile, die als Trennzeichen im Quellstring dienen. Für jeden Platzhalter wird der Textteil zwischen den Literalen extrahiert und automatisch in den passenden Datentyp gewandelt.

// Einzelner Wert → Skalar
lat = sScan('lat: %v', zeile); // => 51.234 (dbl)
port = sScan('port=%d', konfig); // => 8080 (uint)

// Mehrere Werte gleichen Typs → Vektor
v = sScan('lat: %v, lon: %v', zeile); // => [51.234, 12.345] (dbl-Vektor)

// Gemischte Typen → zeilenweise als String, adressierbar mit sLine()
raw = sScan('name: %v, age: %v', s); // => "Alice\n30"
name = sLine(raw, 0); // => "Alice"
age = sLine(raw, 1); // => "30"
VerhaltenBeschreibung
Letzter Platzhalter ohne Abschluss-Literalliest bis zum Stringende
Anführungszeichen um einen Wertwerden automatisch entfernt
Literal im Formatstring nicht gefundengibt cErr zurück
Kein Platzhalter im Formatstringgibt cErr zurück

Formatierung eines Strings "sFormat"

Um einen String f, der z.B. numerische Werte beinhaltet, als String r formatiert darzustellen, kann folgende Funktion benutzt werden

r = sFormat(f, ...);

Hierbei stellt f einen Formatstring in der Art von printf() dar, und es ist zu jedem Platzhalter ein korrespondierendes Argument der Form %[<width>][.<precision>]<type> anzugeben.

  • <width>: optionale Feldbreite für die Ausgabe, width < 0: linksbündig

  • .<precision>: optional für f/g/e, Anzahl der Nachkommastellen oder gültigen Ziffern

  • <type>: Die Argumente werden entsprechend des gewählten Ausgabetyps konvertiert. Zusätzliche Längenangaben für den Datentyp werden deshalb nicht benötigt.

    <type>DatentypAusgabe
    b<bool>Ausgabe von 'false' oder 'true'
    d<int>Ganzzahl mit Vorzeichen, Basis 10
    u<uint>Ganzzahl ohne Vorzeichen, Basis 10
    x, X<uint>Ganzzahl ohne Vorzeichen, Basis 16
    kleine (x) oder große (X) Ziffern A-F
    o<uint>Ganzzahl ohne Vorzeichen, Basis 8
    f<dbl>Fließkommazahl ohne 10er-Exponent
    Beispiel: 1234.5678
    g<dbl>Fließkommazahl in optimierter Darstellung
    wie f oder e je nach Größenordnung der Zahl.
    Beispiele: 1234.45678, 1.2345e9, 1.2345e-12
    e<dbl>Fließkommazahl mit 10er-Exponent
    Beispiel: 1.2345678e3
    s<str>Text

Beispiel:

template = 'alt: %6.2f, lat: %.9f, lon: %.9f';
tx = sFormat(template, 140.4, 49.8765432, -3.14);
// tx := 'alt: 140.40, lat: 49.876543200, lon: -3.140000000';

Zugriff auf den Inhalt einer Ressource "sResource"

Die Funktion liefert den Inhalt der bezeichneten Ressource als konstanten (binären) String.

resStr = sResource({@ref:'myResource'});
warnung

Der Inhalt der Ressource kann je nach Konfiguration nicht nur reinen UTF-8-Text beinhalten (z.B. eine INI-Datei oder ein JSON-Objekt), sondern auch binäre Daten, wie double-Arrays, jpg-Images, etc. Insbesondere binäre Daten dürfen nur von geeigneten Spezialfunktionen weiter verarbeitet werden. Zur Implementierung solcher Funktionen sprechen sie uns gerne an.

Entfernen von Whitespace "sTrim"

Diese Funktion entfernt sämtlichen Whitespace am Anfang und Ende des Arguments, wie z.B. in

s_trimmed = sTrim(s);

Umwandlung in Groß-/Kleinschreibung "sUpper", "sLower"

Die Umwandlung eines Strings bzgl. Groß-/Kleinschreibung erfolgt mittels

s_uppercase = sUpper(s);
s_lowercase = sLower(s);

Vereinfachung "sSimplify" von Strings

Die Funktion sSimplify() ersetzt alle mehrfachen Aufkommen von White-Space (Leerzeichen, Tabulatoren, CR, LF, ...) durch ein einfaches Leerzeichen.

s_simple = sSimplify(s);

Normierung "sNormalize" von Strings

Die Funktion sNormalize() entfernt zueinander passende Anführungszeichen "..."oder '...' am Anfang und Ende des Strings und ersetzt alle \-ESCAPE-Sequenzen durch ihren bezeichneten Code.

s_normalized = sNormalize(s);

Suchen, Finden und Zerlegen

Suchen von Teilzeichenketten "sFind"

Die Suchfunktion sFind liefert den Index r oder eine Matrix mit Position und Länge der gefundenen Textteile des Musters p im String s zurück und kann wie folgt verwendet werden

r1 = sFind(s, {...}); // Konfiguration des 'pattern' ist zwingend
r1 = sFind(s,p); // direkte Suche ab Stringbeginn
r2 = sFind(s,p,b); // direkte Suche ab Position b
// Optionales Konfigurationsobjekt für alle Varianten
rx = sFind(..., { pattern: <string>
, case: <bool>
, all: <bool>
, regex: <bool>
, subex: <bool>
});

Wird kein Muster gefunden, so liefert diese Funktion -1 zurück.

Eine Matrix mit den Suchergebnissen hat folgenden Aufbau:

rx=[s0c0s1c1sn1cn1]rx = \left[ {\begin{array}{cc} {{s_0}}&{{c_0}}\\ {{s_1}}&{{c_1}}\\ \vdots & \vdots \\ {{s_{n - 1}}}&{{c_{n - 1}}} \end{array}} \right]

Diese Matrix kann direkt zusammen der Zeilennummer des Einzelergebnisses an die Funktionen sCopy() oder sErase() übergeben werden.

Eine einzelnes Ergebnis kann mit der Funktion GetRow() oder durch direkte Angabe der Indizierung weiterverarbeitet werden:

part4 = sCopy(s, rx, 3); // counting from zero
part4a = sCopy(s, rx[3,0], rx[3,1]); // equivalent
part4_sc = GetRow(rx, 3); // vector [s3, c3]
part4b = sCopy(s, part4_sc); // equivalent
EigenschaftWertBeschreibung
pattern<str>Konstantes Suchmuster, falls Parameter p nicht verwendet wird.
Ist etwas performanter für regex-Modi, da der Ausdruck nicht immer wieder neu übersetzt werden muss.
case<bool>Suche mit Berücksichtung von Groß- / Kleinschreibung, (def: false, case insensitiv)
all<bool>Liefert einen Vektor mit den Anfangspositionen und Längen der übereinstimmenden Textteile,
nicht in Verbindung mit subex
regex<bool>Interpretiert das Muster pattern oder p als Regular Expression, Ergebnis ist eine Matrix mit Position und Länge der gefundenen Ausdrücke/Teilausdrücke
subex<bool>Liefert Position und Länge der extrahierten Textteile in einer Ergebnismatrix,
setzt regex automatisch

Beispiele

// 0 1 2 3
// 0123456789012345678901234567890123456789
str = 'Hello world and hello my dear friends!';

p0 = sFind(str, "dog");
// => p0 := -1; // not found, independant of selected modes
// my test with (p0 < 0) ? ... : ...
// or isMatrix(p0) ? ... : ... for 'all'- or 'regex'-modes

p1 = sFind(str, "hello");
// => p1 := 0;

p2 = sFind(str, "hello", {case: true});
// => p2 := 16; // first one is now skipped

p3 = sFind(str, "hello", {all: true});
// => p3 := [ [ 0, 5]
// , [16, 5]];

p4 = sFind(str, "(and|dear)", { regex: true, all: true});
// => p4 := [ [12, 3]
// , [25, 4]];

p5 = sFind(str, 'dear\s+(\w+)', { subex: true });
// => p5 := [ [25, 12] // first row is complete match
// , [30, 7]]; // then (...) extractions follow

dear= sCopy(str, p5, 1);
// => dear:= 'friends';

// 0 1 2 3
// 0123456789012345678901234567890123456789
geo = 'lat: 51.234567, lon: 12.3456789';
p6 = sFind(geo, 'lat:\s*([0-9.+-]+)', {subex: true});
// => p6 := [ [ 0,14]
// , [ 5, 9]];
lat = dbl(sCopy(geo, p6, 1));
// => lat := 51.234567;

Ersetzen von Teilzeichenketten "sReplace"

Um (mehrere) Zeichenketten t_k innerhalb der Zeichenkette s durch die jeweils korrespondierenden Text von s_k zu ersetzen, kann folgendes verwendet werden

r = sReplace(s,t_1,s_1, ..., t_N,s_N);

Hierbei findet eine sequentielle Ersetzung in der Reihenfolge der Argumente statt, die Werte s_k werden implizit in eine Textdarstellung konvertiert. Für mehr Kontrolle bei der Konvertierung kann die Funktion sFormat() verwendet werden.

Beispiel:

template = 'alt: <alt>, lat: <lat>, lon: <lon>';
tx = sReplace(template, '<alt>', 140.4, '<lat>', 49.8765432, '<lon>', -3.14);
// tx := 'alt: 140.400000, lat: 49.876543, lon: -3.140000';

Suche nach Key-Value-Kombination "sGetKV" 1

Diese Funktion sucht in einem String (Textblock) s nach dem Schlüssel k und gibt den zugehörigen Wert zurück. Die Suche ist unabhängig von Groß-/Kleinschreibung.

v1 = sGetKV(s, k);
// Optionaler Konfigurationsblock
vx = sGetKV(..., { format: <enum>
, filter: <regex>
, index: <uint>
});
EigenschaftWertBeschreibung
format<enum>Legt das Format des Textblocks fest und setzt den passenden Standard-filter. Mögliche Werte: json (Standard), ini, xml_attr, xml_node
filter<regex>Überschreibt den Format-Standard: regulärer Ausdruck, der — nach dem Schlüsselnamen — den zugehörigen Wert als Capture-Gruppe (...) erfasst
index<uint>Wählt den N-ten Treffer (0-basiert, Standard: 0). Nützlich bei mehrfach vorkommendem Schlüssel, z. B. wiederholten XML-Elementen.

Wird der Schlüssel nicht gefunden (oder der Index überschreitet die Anzahl der Treffer), gibt die Funktion cErr zurück. Anführungszeichen um den gefundenen Wert werden automatisch entfernt.

Format-Varianten

FormatTrennzeichenTypisches Beispiel
json (Standard): nach optionalem Abschluss-Quote{"key": "value", "count": 3}
ini=, Wert bis Zeilenendehost=localhost\nport=8080
xml_attr=, Wert in Anführungszeichen<device id="42" name="sensor"/>
xml_nodeXML-Tag <key>…</key><voltage>3.3</voltage>

Geschachtelte und mehrfach vorkommende Elemente

sGetKV wertet keine Verschachtelungstiefe aus. Bei geschachtelten Objekten (z. B. {"outer": {"inner": 1}}) trifft index:0 auf den äußeren Schlüssel; eine gezielte Suche auf tiefer liegende Werte erfordert eine zweistufige Auswertung: zuerst den Wert des äußeren Schlüssels extrahieren, dann darauf erneut sGetKV anwenden.

Wiederholte Elemente desselben Schlüssels (z. B. <val>10</val><val>20</val>) können mit index selektiert werden.

Beispiele

cfg = '{"name": "Alice", "count": 3}';
name = sGetKV(cfg, 'name'); // => "Alice"
cnt = sGetKV(cfg, 'count'); // => 3 (uint)

ini = 'host=localhost\nport=8080';
port = sGetKV(ini, 'port', {format:'ini'}); // => 8080 (uint)

xml = '<data><voltage>3.3</voltage></data>';
u = sGetKV(xml, 'voltage', {format:'xml_node'}); // => 3.3 (dbl)

// Zweites Vorkommen eines wiederholten XML-Elements
seq = '<data><val>10</val><val>20</val><val>30</val></data>';
v1 = sGetKV(seq, 'val', {format:'xml_node'}); // => 10
v2 = sGetKV(seq, 'val', {format:'xml_node', index:1}); // => 20

// Eigener Filter für abweichende Syntax (key => value; ...)
v = sGetKV(s, 'temp', {filter:'\s*=>\s*([^;]+)'});

Aufteilen von Zeichenketten "sSplit"

Um eine Zeichenkette an einem Zeichen ch aufzuteilen, kann die Funktion sSplit verwendet werden. Diese liefert eine zweispaltige Matrix aller Startpositionen und Längen der korrespondierenden Teilzeichenketten zurück

r1 = sSplit(s, ch);
// Optionaler Konfigurationsblock
rx = sSplit(..., { trim: <bool>
});
pos = rx[0, 0];
len = rx[0, 1];
EigenschaftWertBeschreibung
trim<bool>Entfernt White-Space am Anfang und Ende der Elemente

Anzahl der Zeilen innerhalb eines Strings "sLines"

Die Anzahl der Zeilen eines Strings s kann wie folgt ermittelt werden

lines = sLines(s);

Dabei werden alle Variante von Zeilenumbrüchen unterstützt (LF, CRLF, LFCR, CR).

Extraktion einer Zeile aus einem mehrzeiligen String "sLine"

Um die i-te Zeile aus einem String s zu extrahieren, bietet sich folgende Funktion an

lineI = sLine(s,i);
// Optionales Konfigurationsobjekt
lineX = sLine(..., { trim: <bool>
});
EigenschaftWertBeschreibung
trim<bool>Entfernt White-Space am Anfang und Ende der Zeile

Einlesen vollständiger Zeilen "sGetLine"

Die Funktion sGetLine() verbindet die einzelnen Werte aus dem String-Kanal s und speichert diese, bis eine vollständige Zeile ausgegeben werden kann. Diese wird aus dem Puffer entfernt und mit dem verbleibenden Rest wird die Akkumulation fortgesetzt.

Um aus einem Stream von Zeichen eine vollständige Zeile zu erhalten, kann folgendes verwendet werden

line1 = sGetLine(s);
// Optionales Konfigurationsobjekt
lineX = sGetLine(..., { trim: <bool>
, eoln: <str>
, timeout: <dbl>
});
EigenschaftWertBeschreibung
trim<bool>Die ausgegebene Zeile wird noch von Leerzeichen bereinigt
eoln<string>Definition des Zeilenabschlusses
timeout<dbl>Zeit in Sekunden nach dem letzte Fragment, bis ein Zeilenende eingefügt wird.

Footnotes

  1. Ab Katalog Version 13 verfügbar. 2