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 Stringss_connected = s1 + s2;tippstr(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 FunktionsFormat(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"
| Verhalten | Beschreibung |
|---|---|
| Letzter Platzhalter ohne Abschluss-Literal | liest bis zum Stringende |
| Anführungszeichen um einen Wert | werden automatisch entfernt |
| Literal im Formatstring nicht gefunden | gibt cErr zurück |
| Kein Platzhalter im Formatstring | gibt 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>Datentyp Ausgabe 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) ZiffernA-Fo <uint>Ganzzahl ohne Vorzeichen, Basis 8 f <dbl>Fließkommazahl ohne 10er-Exponent
Beispiel: 1234.5678g <dbl>Fließkommazahl in optimierter Darstellung
wie f oder e je nach Größenordnung der Zahl.
Beispiele: 1234.45678, 1.2345e9, 1.2345e-12e <dbl>Fließkommazahl mit 10er-Exponent
Beispiel: 1.2345678e3s <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'});
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:
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
| Eigenschaft | Wert | Beschreibung |
|---|---|---|
| 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>
});
| Eigenschaft | Wert | Beschreibung |
|---|---|---|
| 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
| Format | Trennzeichen | Typisches Beispiel |
|---|---|---|
json (Standard) | : nach optionalem Abschluss-Quote | {"key": "value", "count": 3} |
ini | =, Wert bis Zeilenende | host=localhost\nport=8080 |
xml_attr | =, Wert in Anführungszeichen | <device id="42" name="sensor"/> |
xml_node | XML-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];
| Eigenschaft | Wert | Beschreibung |
|---|---|---|
| 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>
});
| Eigenschaft | Wert | Beschreibung |
|---|---|---|
| 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>
});
| Eigenschaft | Wert | Beschreibung |
|---|---|---|
| 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. |