Here an instance (myString) of a GSB.UTF8String with a capacity of 128 bytes is created and the string segment with the value of
a STRING variable (Roman numeral 1968) is initialized. The methods of STR.IString are available.
From STRING to IString
VAR
myString : GSB.UTF8String<128> := (sValue := UTF8#'CMℒ✖Ⅷ'); // roman 1968
pbySegment : POINTER TO BYTE;
udiSize : UDINT;
xOk : BOOL;
END_VAR
pbySegment := myString.GetSegment(udiSize=>udiSize);
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
);
Function of the StringBuilder
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 ⅩⅨ. Ѻℓƴμρ☤ṧḉнεη $$ρї℮łℯ α♭ℊεℌαʟ⊥℮ᾔ.';
pbyValue : POINTER TO BYTE;
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 := pbyValue := myResult.GetSegment();
pbyValue[udiLength] := 0; // the content of a variable of type STRING must be terminated with 0.
xOk := (psResult^ = sResult); // Both memory areas should have the same content.
In the example above, an instance of the builder is created with an initial capacity
of 64 bytes (udiInitialCapacity) and a dynamic factor of 50 (usiExtensionFactor). The string generated further above is still passed in the declaration, and as a
result the builder is filled with this string (UTF8#'CMℒ✖Ⅷ'). Using the usiExtensionFactor parameter, increases the builder by 50% when its current capacity is used up.
Reading a file with the 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
Analysis of the contents of a builder instance
VAR
myRange : SBD.Range := (itfBuilder := myBuilder);
diRune : SBD.RUNE;
eError : STR.ERROR;
END_VAR
myRange.Reset();
WHILE (diRune := myRange.GetNextRune(eErrorID=>eErrorID)) <> 0 AND 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
For passing UTF-8 encoded contents, no cache is needed for encoding conversion because the data is already UTF-8 encoded in the builder. Therefore, the contents of a segments of a builder can be sent directly, for example over a TCP/IP connection.
Transporting the UTF-8 encoded contents of a builder over the network
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 *)