Skip to main content

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.

Beispiel 9. Von STRING zu IString
VAR
    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
);


Beispiel 10. Funktion des String-Builders
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.

Beispiel 11. Einlesen einer Datei mit dem Builder
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_IF


Beispiel 12. Analyse des Inhalts einer Builder-Instanz
VAR
    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_WHILE


Fü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.

Beispiel 13. Transport des UTF-8 kodierten Inhalts eines Builders über das Netzwerk
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.

Beispiel 14. Arbeiten mit StringPool und RangePool
VAR
    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_WHILE


Arbeiten 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

IsControl

Erkennt allgemeine Steuerzeichen

IsLetter

Erkennt Buchstaben im weiteren Sinne

IsMark

Erkennt kombinierende also beispielsweise diakritische Zeichen

IsDigit

Erkennt dezimale Ziffern

IsLower

Erkennt Kleinbuchstaben

IsNumber

Erkennt Ziffern und Zeichen, die für Zahlen Anwendung finden

IsGraphic

Erkennt nur druckbare Zeichen (inklusive unterschiedlicher Leerzeichen)

IsUpper

Erkennt Großbuchstaben

IsPunct

Erkennt Zeichen, die zur Interpunktion verwendet werden

IsPrint

Erkennt nur druckbare Zeichen (berücksichtigt als Leerzeichen nur 16#20)

IsTitle

Erkennt Großbuchstaben für Überschriften

IsSpace

Erkennt Leerzeichen unterschiedlicher Breite, Zeilenumbruch, ...

IsSymbol

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.

Beispiel 15. Zeichenanalyse
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_WHILE


Umwandlung von Zeichen

  • Buchstaben in Großbuchstaben umwandeln (UC.ToUpper)

  • Buchstaben in Kleinbuchstaben umwandeln (UC.ToLower)

Beispiel 16. Umwandlung
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 = Dz


Vergleich von Zeichenketten

  • Mit Berücksichtigung der Groß-/Kleinschreibung (STR.Compare)

  • Ohne Berücksichtigung der Groß-/Kleinschreibung (UC.EqualFold)

Beispiel 17. Vergleich von Strings
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()
);