Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

STRING

A STRING is STRING[80]; a STRING[n] holds up to n characters, one byte each, read as Latin-1, followed by a 0 byte: n + 1 bytes in all, as on CODESYS and TwinCAT. n is at most 65535. A literal with a character above U+00FF is refused (E0020): write it as a WSTRING. $N is a line feed.

Strings compare by their characters’ codes, left to right, a shorter string as if extended with zeros (Ed.3 6.6.2.5.11): 'Z' > 'AZ', and 'ab' < 'abc'.

Too long: cut, and flagged

A string stored where it does not fit is cut to fit and flagged STRING_LENGTH. Every vendor cuts; iecf also says so. A function’s result is never cut on its way: its buffer holds the most its inputs can make, so only the store at the end can cut. A FUNCTION’s or method’s STRING input is cut to its length as the function starts, flagged at the input’s declaration; its STRING result is its own declared length, and is cut by what it is stored in, as any string. A STRING in-out is given a variable of its length exactly (E0013 otherwise), by name or in order alike, STRING being STRING[80]; and the harness case fnparams runs each.

ExpressionTypeResultFlagNote
CONCAT('ab', 'cd')STRING'abcd'—
CONCAT('ab', 'cd')STRING[3]'abc'flag STRING_LENGTHcut where it is stored
CONCAT('ab', 'cd', 'ef')STRING'abcdef'—CONCAT takes any number
'Z' > 'AZ'BOOLTRUE—
'ab' < 'abc'BOOLTRUE—
LEN('')INT0—

Lengths and positions outside the string

LEFT, RIGHT, MID, DELETE, INSERT and REPLACE take a length L and a position P, counted from 1. The standard calls a negative L or P, or a position past the end, an error and leaves what it does to the implementer. iecf gives what Siemens documents for each case — the one vendor that documents them all — and flags STRING_POSITION; the task goes on. A length of 0 takes nothing and is no error; INSERT at P = 0 puts IN2 first, as CODESYS and Siemens do.

ExpressionTypeResultFlagNote
LEFT('abc', 2)STRING'ab'—
LEFT('abc', 0)STRING''—no error
LEFT('abc', 5)STRING'abc'flag STRING_POSITIONL past the end: all of IN
LEFT('abc', -1)STRING''flag STRING_POSITIONL < 0: empty
RIGHT('abc', 2)STRING'bc'—
RIGHT('abc', 5)STRING'abc'flag STRING_POSITION
MID('abcde', 2, 2)STRING'bc'—MID(IN, L, P)
MID('abc', 5, 2)STRING'bc'flag STRING_POSITIONL past the end: from P to the end
MID('abc', 2, 0)STRING''flag STRING_POSITIONP < 1: empty
MID('abc', 1, 4)STRING''flag STRING_POSITIONP past the end: empty
DELETE('abcde', 2, 2)STRING'ade'—DELETE(IN, L, P)
DELETE('abc', 2, 5)STRING'abc'flag STRING_POSITIONP past the end: IN unchanged
DELETE('abcde', 9, 2)STRING'a'flag STRING_POSITIONL past the end: to the end
INSERT('abc', 'XY', 2)STRING'abXYc'—after the P-th character
INSERT('abc', 'XY', 0)STRING'XYabc'—P = 0: first
INSERT('abc', 'XY', 5)STRING'abcXY'flag STRING_POSITIONP past the end: appended
REPLACE('abcde', 'X', 2, 2)STRING'aXde'—REPLACE(IN1, IN2, L, P)
REPLACE('abc', 'X', 2, 9)STRING'abcX'flag STRING_POSITIONP past the end: appended
FIND('abcbc', 'bc')INT2—the first
FIND('abc', 'x')INT0—not there
FIND('abc', '')INT0—

LEN and FIND give INT, as CODESYS, TwinCAT and Siemens do.

MOVE, SEL, MUX, MIN, MAX and LIMIT take strings as they take numbers (Ed.3 Tables 31, 32): every input is evaluated, then one is given; MIN, MAX and LIMIT order strings as the comparisons do. MUX with K outside its inputs faults, as for any type.

ExpressionTypeResultFlagNote
SEL(TRUE, 'ab', 'cd')STRING'cd'—
MUX(INT#1, 'a', 'b', 'c')STRING'b'—K counts from 0
MUX(INT#3, 'a', 'b', 'c')STRING—fault MUXno IN3
MAX('ab', 'abc', 'Z')STRING'abc'—by codes: 'a' > 'Z'
MIN('ab', 'abc')STRING'ab'—
LIMIT('b', 'z', 'd')STRING'd'—
MOVE('xy')STRING[1]'x'flag STRING_LENGTHcut where it is stored
MAX("a", "$263A")WSTRING"$263A"—

CHAR

A CHAR holds one Latin-1 character, its code a byte (Ed.3 Table 10); it starts as '$00'. CHAR#'X' and CHAR#16#58 are CHAR literals; an untyped 'X' of one character is a CHAR where a CHAR is wanted - given to a CHAR, or compared with one (Ed.3 Table 6, 1b) - and a STRING elsewhere. A CHAR goes where a STRING goes, as a string of that one character (Figure 11: implicit), and '$00' there is the empty string, as '$00' ends any string.

CHAR_TO_BYTE, BYTE_TO_CHAR and CHAR_TO_WORD and wider are binary transfers (Table 25), CHAR_TO_USINT the code (Figure 11). STRING_TO_CHAR takes the first character (Table 27); of an empty string, for which Ed.3 leaves the result undefined, it gives '$00' and is flagged CONVERSION, as any value its target cannot hold (dependencies).

ExpressionTypeResultFlagNote
CHAR#'a'CHAR'a'—
CHAR#16#41CHAR'A'—
BYTE_TO_CHAR(BYTE#16#42)CHAR'B'—
CHAR_TO_BYTE(CHAR#'a')BYTE16#61—
CHAR_TO_USINT('a')USINT97—'a' a CHAR here
CHAR_TO_WORD(CHAR#'$FF')WORD16#00FF—
CHAR#'a' < CHAR#'b'BOOLTRUE—by code
CHAR#'a' = 'a'BOOLTRUE—
MAX(CHAR#'a', CHAR#'b')CHAR'b'—
STRING_TO_CHAR('xyz')CHAR'x'—the first character
STRING_TO_CHAR('')CHAR'$00'flag CONVERSIONundefined in Ed.3
CHAR_TO_STRING(CHAR#'q')STRING'q'—
CONCAT('ab', CHAR#'c')STRING'abc'—
CHAR_TO_STRING(CHAR#'$00')STRING''—the empty string

One character: s[i]

s[i] is the i-th character of a STRING, counted from 1, a CHAR (Ed.3 6.4.2); it reads and it is written: c := s[2];, s[3] := 'B';, a[1][2] in an array of strings. Ed.3 gives the functions it is equivalent to, STRING_TO_CHAR(MID(s, 1, i)) and REPLACE(s, c, 1, i), and leaves a position outside the string open:

  • a read outside the string gives '$00', flagged STRING_POSITION, as that MID and STRING_TO_CHAR give;
  • a write changes only a character the string has: below 1 or past its end, s is left as it is, flagged STRING_POSITION - it neither grows nor empties;
  • s[i] := '$00' cuts the string there, as '$00' ends any string: 'ABCD' becomes 'A' for i = 2. CODESYS and TwinCAT cut there too; Siemens, whose STRING keeps its length in a byte, keeps a '$00' inside, which an iecf STRING cannot hold.

A position that is a constant outside 1..n of a STRING[n] is an error (E0014). s[i] is not a variable: no REF() of it (E0026), and it is no in-out’s (E0015). A STRING is not stored in a CHAR, so s[2] := t for a STRING t is E0013 (Ed.3’s error cases); a CHAR goes into a STRING. CODESYS and TwinCAT count from 0 and give a BYTE, not Ed.3’s; Siemens counts from 1 and gives a CHAR, as iecf. None documents a position outside the string.

In these rows s is a STRING[10] holding 'ABCD':

ExpressionTypeResultFlagNote
s[2]CHAR'B'—from 1
s[4]CHAR'D'—the last
s[INT#2 * 2 - 1]CHAR'C'—computed
s[5]CHAR'$00'flag STRING_POSITIONpast the end, inside STRING[10]
s[LEN(s) - 4]CHAR'$00'flag STRING_POSITION0
s[1] = 'A'BOOLTRUE—
CONCAT(s[4], s[1])STRING'DA'—a CHAR as a string

The harness case stringchars writes characters, past the end and below 1 too (s unchanged, flagged), with values worked by hand.

WSTRING and WCHAR

A WSTRING is WSTRING[80]; a WSTRING[n] holds up to n UTF-16 units, 16 bits each, followed by a 0: Ed.3 Table 27 names UTF-16. A WCHAR is one unit, starting as "$0000". A character above U+FFFF - an emoji, a rare CJK character - is two units in a WSTRING, a surrogate pair, and LEN, positions and ws[i] count both; a WCHAR literal cannot hold one (E0020). CODESYS calls its WSTRING UCS-2, the same units for every character up to U+FFFF.

A WSTRING literal is written in double quotes, "ab", its escapes $hhhh with four hex digits ("$263A") or ${1F600}; WSTRING#'ab' and WCHAR#'X' take single quotes and $hh (Ed.3 Table 6, 4c and 4d). WCHAR#"X", WCHAR#16#263A, and an untyped "X" of one character where a WCHAR is wanted are WCHARs; 'X' never is (E0013), nor is "X" a CHAR. A WCHAR goes where a WSTRING goes (Figure 11: implicit); STRING and WSTRING, CHAR and WCHAR do not mix without a conversion (Ed.3 6.6.2.5.11).

LEFT, RIGHT, MID, DELETE, INSERT, REPLACE, FIND, LEN, CONCAT, comparison and ws[i] work on WSTRINGs as on STRINGs, unit by unit, with the same flags.

At an address, a WSTRING[n] takes 2(n + 1) bytes from its byte, an odd one too, each unit little-endian, as any part there; %QW1 addresses byte 1. A reference to one reaches it there (References).

Between the two widths (Ed.3 Table 27): a STRING’s characters are Latin-1, U+0000 to U+00FF, and carry across as they are. A character a STRING cannot hold, WSTRING_TO_STRING skips - a surrogate pair whole - and WCHAR_TO_CHAR gives '$00'; both are flagged CONVERSION. Beckhoff documents the same skip for its WSTRING_TO_STRING2 and WCHAR_TO_CHAR; CODESYS and Siemens document nothing (dependencies). WSTRING_TO_WCHAR gives the first unit, "$0000" flagged for an empty string, as STRING_TO_CHAR does; Figure 11 lists no such conversion, but Table 27 defines it, and iecf follows Table 27. WCHAR_TO_WORD, _DWORD and _LWORD and WORD_TO_WCHAR are binary transfers (Table 25).

Numbers and the other types to and from a WSTRING (INT_TO_WSTRING, WSTRING_TO_REAL, TO_WSTRING) give and read the same text as to and from a STRING (Text conversions); a character outside ASCII is none a literal has.

ExpressionTypeResultFlagNote
LEN("a$263Ab")INT3—
LEN("${1F600}")INT2—a surrogate pair
CONCAT("ab", "cd")WSTRING[3]"abc"flag STRING_LENGTHcut where it is stored
MID("a$263Ab", 1, 2)WSTRING"$263A"—
FIND("a$263Ab", "b")INT3—
"Z" > "AZ"BOOLTRUE—
WCHAR#16#263A > WCHAR#'z'BOOLTRUE—by unit
WCHAR#"x" = "x"BOOLTRUE—"x" a WCHAR here
CONCAT("a", WCHAR#16#263A)WSTRING"a$263A"—a WCHAR as a string
WSTRING_TO_STRING("caf$00E9")STRING'caf$E9'—Latin-1 kept
WSTRING_TO_STRING("a$263Ab")STRING'ab'flag CONVERSIONskipped
WSTRING_TO_STRING("a${1F600}b")STRING'ab'flag CONVERSIONa pair skipped whole
STRING_TO_WSTRING('caf$E9')WSTRING"caf$00E9"—
WCHAR_TO_CHAR(WCHAR#16#E9)CHARCHAR#16#E9—
WCHAR_TO_CHAR(WCHAR#16#263A)CHAR'$00'flag CONVERSION
CHAR_TO_WCHAR(CHAR#'q')WCHAR"q"—
WSTRING_TO_WCHAR("xyz")WCHAR"x"—the first character
WSTRING_TO_WCHAR("")WCHAR"$0000"flag CONVERSIONundefined in Ed.3
WCHAR_TO_WORD(WCHAR#16#263A)WORD16#263A—
INT_TO_WSTRING(-7)WSTRING"-7"—
WSTRING_TO_INT("42")INT42—

The harness case wstrings runs them in a program: in an array and a structure, a function block’s input and output, ws[i] written.