Beispiele
Um die Stringverarbeitung so einfach wie möglich zu gestalten, steht Ihnen die Bibliothek Generic String Base zur Verfügung. Sie verwendet allerdings das Compilerfeature VAR_GENERIC CONSTANT und kann somit erst ab CODESYS V3.5 SP19 Patch 5 genutzt werden.
Bibliothek Generic String Base
Hier wird eine Instanz (myString) eines GSB.UTF8Strings mit einer Kapazität von 128 Bytes erzeugt und das String-Segment mit dem Wert einer STRING-Variablen (Römische Zahl 1968) initialisiert. Es stehen die Methoden von STR.IString zur Verfügung.
STRING zu IStringVAR
myString : GSB.UTF8String<128> := (sValue := UTF8#'𝕄CMℒ✖Ⅷ'); // römisch 1968
psString : POINTER TO STRING; udiSize, udiLength : UDINT; xASCII, xOk : BOOL;
END_VAR
psString := myString.GetSegment(udiSize=>udiSize, udiLength=>udiLength, xASCII=>xASCII); // Conversion back to the STRING data type
xOk := (
myString.IsValid() AND // A valid UTF-8 encoding is present
udiSize = 128 AND // The capacity of the string in bytes
myString.Len() = 17 AND // The current length of the string in bytes
STR.RuneCount(myString) = 6 // The current number of characters in the string
);VAR
myString : GSB.UTF8String<20> := (sValue := UTF8#'𝕄CMℒ✖Ⅷ'); // roman 1968
sValue : STRING := 'wurden in Mexico-Stadt die';
wsValue : WSTRING := "ⅩⅨ.";
diSpace : STR.RUNE := 32;
myValue : GSB.UTF8String<128> := (sValue := UTF8#'Ѻℓƴμρ☤ṧḉнεη $$ρї℮łℯ α♭ℊεℌαʟ⊥℮ᾔ.');
myBuilder : GSB.Builder<(*udiInitialCapacity*) 64, (*usiExtensionFactor*) 50> := (itfString:=myString);
myResult : GSB.UTF8String<128>;
{attribute 'monitoring_encoding' := 'UTF-8'}
sResult : STRING(128) := UTF8#'𝕄CMℒ✖Ⅷ wurden in Mexico-Stadt die ⅩⅨ. Ѻℓƴμρ☤ṧḉнεη $$ρї℮łℯ α♭ℊεℌαʟ⊥℮ᾔ.';
psResult : POINTER TO STRING;
udiLength : UDINT;
xOk : BOOL;
END_VAR
myBuilder.WriteRune(diSpace);
myBuilder.WriteString(sValue);
myBuilder.WriteRune(diSpace);
myBuilder.WriteWString(wsValue);
myBuilder.WriteRune(diSpace);
myBuilder.WriteIString(myValue);
udiLength := myBuilder.Len(); // The number of bytes occupied in the builder.
myBuilder.ToIString(myResult); // The individual parts of the string are copied together to myResult.
psResult := myResult.GetSegment();
xOk := (psResult^ = sResult); // Both memory areas should have the same content.Im obigen Beispiel wird eine Instanz des Builders mit einer initialen Kapazität von 64 Bytes (udiInitialCapacity) und einem Dynamikfaktor von 50 (usiExtensionFactor) erzeugt. Der weiter oben erzeugte String wird noch in der Deklaration übergeben, somit wird der Builder mit diesem String (UTF8#'𝕄CMℒ✖Ⅷ') befüllt. Über den Parameter usiExtensionFactor wird erreicht, dass der Builder automatisch um 50 % vergrößert wird, wenn seine aktuelle Kapazität erschöpft ist.
VAR
sPath : STRING := 'myFilePath';
hFile : RTS_IEC_HANDLE := RTS_INVALID_HANDLE;
myBuilder : GSB.Builder<(*udiInitialCapacity*) 16#10000, (*usiExtensionFactor*) 50>;
abyBuffer : ARRAY[0..4095] OF BYTE;
pbyData : POINTER TO BYTE;
udiSize : UDINT;
udiCount : UDINT;
eEncoding : SCV.ENCODING;
eErrorID : SCV.ERROR;
udiResult : RTS_IEC_RESULT;
END_VAR
hFile := SysFileOpen(sPath, ACCESS_MODE.AM_READ, ADR(udiResult));
IF udiResult <> ERRORS.ERR_OK THEN
// handle error condition
RETURN;
END_IF
REPEAT // fake loop - We need the EXIT feature
pbyData := ADR(abyBuffer);
udiSize := TO_UDINT(SysFileRead(hFile, pbyData, XSIZEOF(abyBuffer), ADR(udiResult)));
IF udiResult <> ERRORS.ERR_OK THEN
// handle error condition
EXIT;
END_IF
// Determination of the file encoding
udiCount := SCV.DecodeBOM(pbyData, udiSize, eEncoding=>eEncoding, eErrorID=>eErrorID);
IF eErrorID <> 0 THEN
// handle error condition
EXIT;
END_IF
pbyData := pbyData + udiCount;
udiSize := udiSize - udiCount;
WHILE udiSize > 0 DO
// Convert file content to UTF-8 and copy to Builder-Content
udiCount := myBuilder.WriteMemSegment(pbyData, udiSize, eEncoding, eErrorID=>eErrorID);
IF eErrorID <> 0 THEN
// handle error condition
EXIT;
END_IF
pbyData := ADR(abyBuffer);
udiSize := TO_UDINT(SysFileRead(hFile, pbyData, XSIZEOF(abyBuffer), ADR(udiResult)));
IF udiResult <> ERRORS.ERR_OK THEN
// handle error condition
EXIT;
END_IF
END_WHILE
UNTIL TRUE
END_REPEAT
IF hFile <> RTS_INVALID_HANDLE THEN
SysFileClose(hFile);
hFile := RTS_INVALID_HANDLE;
udiCount : UDINT;
END_IFVAR
myRange : SBD.Range := (itfBuilder := myBuilder);
diRune : STR.RUNE;
eError : STR.ERROR;
END_VAR
myRange.Reset();
WHILE (diRune := myRange.GetNextRune(eErrorID=>eErrorID)) <> 0 AND_THEN eErrorID = 0 DO
IF UC.IsSpace(diRune) THEN
// The characters in the builder which are considered as spaces according to UNICODE are counted.
udiCount := udiCount + 1;
END_IF
END_WHILEFür die Weitergabe von UTF-8-kodierten Inhalten wird kein Zwischenspeicher für die Umwandlung der Kodierung benötigt, weil die Daten im Builder bereits UTF-8-kodiert sind. Daher können die Inhalte der Segmente eines Builders direkt, beispielsweise über eine TCP/IP-Verbindung, verschickt werden.
VAR
itfConnection : NBS.IConnection;
pbySegment : POINTER TO BYTE;
udiSize : UDINT;
eError : NBS.ERROR;
END_VAR
(* Provide an active itfConnection *)
pbySegment:= myBuilder.GetFirstSegment(udiSize=>udiSize, eErrorID=>eErrorID);
WHILE pbySegment <> 0 AND eErrorID = 0 DO
eError := itfConnection.Write(pbySegment, udiSize, udiCount=>udiCount);
IF eError <> 0 OR udiCount <> udiSize THEN
// Handle Error
EXIT;
END_IF
pbySegment := myBuilder.GetNextSegment(pbySegment, udiSize=>udiSize, eErrorID=>eErrorID);
END_WHILE
(* e.g. Close itfConnection *)Arbeiten mit den Funktionsbausteinen StringPool und RangePool
Der folgende Code zeigt die Verwendung von dynamischen IString-Instanzen aus einem StringPool. Ein StringPool oder ein RangePool ist gut dafür geeignet, an unterlagerte Programmteile weitergegeben zu werden. Diese können dann bei Bedarf die entsprechenden Instanzen aus dem jeweiligen Pool erzeugen, damit arbeiten und diese Instanzen anschlierßend in den Pool zurücklegen.
StringPool und RangePoolVAR
myString : GSB.UTF8String<256> := (sValue:=UTF8#'Was du nicht willst, dass man dir tu’, das füg auch keinem andern zu.');
myRange : STR.Range := (itfString:=myString);
myStringPool : GSB.StringPool<(*udiStringSize*) 30, (*udiInitialCapacity*) 25, (*usiExtensionFactor*) 0>;
myRangePool : GSB.RangePool<GSB.RANGE_TYPE.ISTRING, (*udiInitialCapacity*) 10, (*usiExtensionFactor*) 0>;
diRune : STR.RUNE;
eErrorID : STR.ERROR;
itfSubString : STR.IString;
liStart, liEnd : LINT;
udiCount : UDINT;
END_VAR
myRange.Reset();
// Decompose myString into substrings and analyze them via a subroutine.
WHILE (diRune:=myRange.GetNextRune(eErrorID=>eErrorID)) <> 0 AND eErrorID = 0 DO
IF diRune = 16#2C (*,*) OR diRune = 16#2E (*.*) THEN
itfSubString := myStringPool.GetString();
IF itfSubString = 0 THEN
(* Handle Error *)
EXIT;
END_IF
myString.ToIString(itfSubString, liStart+1, liEnd, eErrorID=>eErrorID);
IF eErrorID <> 0 THEN
(* Handle Error *)
EXIT;
END_IF
// Analyse the substring and use pool's
// Will release itfSubString
udiCount := Analyse(itfSubString, myStringPool, myRangePool);
(* ... Handle Result ... *)
IF diRune = 16#2E (*.*) THEN
EXIT;
END_IF
diRune:=myRange.GetNextRune(eErrorID=>eErrorID);
IF diRune = 16#20 (* space *) AND eErrorID = 0 THEN
liEnd := liEnd + 1;
ELSE
myRange.UngetLastRune();
END_IF
liStart := liEnd + 1;
END_IF
liEnd := liEnd + 1;
END_WHILEArbeiten mit den Zeichenkategorien von Unicode
Der Unicode-Standard hat das Ziel, weltweit alle Zeichen digital zu erfassen und ihre Eigenschaften zu beschreiben. Dabei werden die Zeichen zu Gruppen (Kategorien) zusammengefasst. In der Unicode-Bibliothek finden sich Funktionen, die ein Zeichen hinsichtlich der Zugehörigkeit zu einer Kategorie prüfen. Diese Funktionen geben TRUE zurück, wenn das übergebene Zeichen zu der jeweiligen Kategorie gehört, ansonsten wird FALSE zurückgegeben.
Name | Funktion |
|---|---|
| Erkennt allgemeine Steuerzeichen |
| Erkennt Buchstaben im weiteren Sinne |
| Erkennt kombinierende also beispielsweise diakritische Zeichen |
| Erkennt dezimale Ziffern |
| Erkennt Kleinbuchstaben |
| Erkennt Ziffern und Zeichen, die für Zahlen Anwendung finden |
| Erkennt nur druckbare Zeichen (inklusive unterschiedlicher Leerzeichen) |
| Erkennt Großbuchstaben |
| Erkennt Zeichen, die zur Interpunktion verwendet werden |
| Erkennt nur druckbare Zeichen (berücksichtigt als Leerzeichen nur |
| Erkennt Großbuchstaben für Überschriften |
| Erkennt Leerzeichen unterschiedlicher Breite, Zeilenumbruch, ... |
| Erkennt Symbole im weiteren Sinne, beispielsweise mathematische Symbole und Währungssymbole. |
Die Inhalte einer IString- oder IBuilder-Instanz lassen sich über einen passenden Baustein vom Typ Range "zeichenweise" analysieren. Für die Analyse lassen sich die Funktionen aus der Unicode-Bibliothek gut verwenden.
VAR
myString : GSB.UTF8String<50> := (sValue:='Hello World!');
myBuilder : GSB.Builder<100, 0> := (itfString:=myString);
mySRange : STR.Range := (itfString:=myString);
myBRange : SBD.Range := (itfBuilder:=myBuilder);
diSRune, diBRune : STR.RUNE;
eErrorID : STR.ERROR;
udiCount : UDINT;
END_VAR
WHILE (diSRune:=mySRange.GetNextRune(eErrorID=>eErrorID)) <> 0 AND eErrorID = 0 DO
diBRune := myBRange.GetNextRune();
IF diSRune <> diBRune THEN
(* Solle nicht vorkommen *)
END_IF
IF UC.IsSpace(diSrune) THEN
udiCount := udiCount + 1;
END_IF
END_WHILEUmwandlung von Zeichen
Buchstaben in Großbuchstaben umwandeln (
UC.ToUpper)Buchstaben in Kleinbuchstaben umwandeln (
UC.ToLower)
VAR
diRuneA, diRuneB : STR.RUNE;
END_VAR
diRuneA := 16#1F3; // U+01F3 = Dž
diRuneB := UC.ToUpper(diRuneA); // U+01F1 = DZ
diRuneA := UC.ToLower(diRuneB); // U+01F3 = Dž
diRuneB := UC.ToTitle(diRuneA); // U+01F2 = DzVergleich von Zeichenketten
Mit Berücksichtigung der Groß-/Kleinschreibung (
STR.Compare)Ohne Berücksichtigung der Groß-/Kleinschreibung (
UC.EqualFold)
VAR
myFirstString : GSB.UTF8String<50> := (sValue:='test');
mySecondString : GSB.UTF8String<50> := (sValue:='Test');
myThirdString : GSB.UTF8String<50> := (sValue:='CoDeSys');
myFourthString : GSB.UTF8String<50> := (sValue:='CODESYS');
diResult : DINT;
xEqual : BOOL;
END_VAR
/// Comparing two Strings lexicographically
/// diResult = 1 --> myFirstString > mySecondString
diResult := STR.Compare(myFirstString, mySecondString);
/// Unicode defined simple case folding
/// xEqual = TRUE --> myThirdString == myFourthString
xEqual := UC.EqualFold(
ADR(myThirdString.sValue), myThirdString.Len(),
ADR(myFourthString.sValue), myFourthString.Len()
);