Table of Contents

6.1.2.11 Strings

Note

This section describes the implementation of MS-VBAL §6.1.2.11 Strings.

The Strings module is represented in the SDK by the interface IStdStringsModule.

The Strings module is declared in full. MS-VBAL §6.1.2.11 has 43 subsections, and RD-VBA's Strings module declares 55 members.

Some MS-VBAL subsections declare more than one member:

  • the B-suffixed byte variants (InStrB, LeftB, LenB, MidB, RightB) share a subsection with the member they vary;
  • the $-suffixed String-returning twins (Chr$, ChrB$, ChrW$) share a subsection with the member they vary;
  • the LTrim / RTrim / Trim group is one subsection, and so is its $-suffixed twin.

Every member has a runtime implementation (StdStrings).

Arguments

An argument has been through the call site before a member is given it (RD-VBAL §5.3.1.11 Procedure Invocation Argument Processing):

  • An argument to a typed parameter was Let-coerced to its type, so Null given for a String or a Long is error 94 before any member is reached: Asc(Null), InStrRev(Null, "a"), StrReverse(Null), String(Null, "a").
  • A typed optional argument left out is its declared default: InStrRev's Start and Replace's Count are -1, Replace's Start is 1, Split's Limit is -1, Filter's Include is True, the NumDigitsAfterDecimal of FormatCurrency, FormatNumber and FormatPercent is -1, and the Abbreviate of MonthName and WeekdayName is False. C# cannot state those defaults, so the declarations do, with StdLibDefaultAttribute (RD-VBAL §6.0.1 Symbol Injection).
  • A Compare left out is the calling module's Option Compare (Compare and Option Compare).
  • A Variant optional argument left out, or a caller's own Missing passed on to one, is left out to the member, and nothing else is: an explicit Empty is Empty. Mid(s, 2, v), with v an omitted Optional of the caller's, is the rest of s from 2. A caller's Missing passed on to a typed optional (Replace's Count, Split's Limit) is error 13, as Missing into any typed destination is.

A member reads a Variant argument as a parameter of the type it needs would be Let-coerced. Read as a String, a number or a date is written in the environment's regional settings (RD-VBAL §5.5.1.2.4). Read as a number (Mid's Length, InStr's start, String's character code), Empty is 0, True is -1, a number is rounded to even, a value beyond a Long is error 6, Null is error 94, and anything else that is not a number is error 13.

An object given for a Variant parameter is read as its Let-coercion reads it (RD-VBAL §5.5.1.2.13): as its "simple data value", what its default member returns when it is called with no arguments, read again if that is an object. So Left(o, 2) of an object whose default member returns "duck" is "du", and one that returns Null gives Null. Nothing is error 91, as the call site makes it for a typed parameter: Left(Nothing, 1) is error 91, as StrReverse(Nothing) is. An object with no default member is error 438, and one whose default member takes an argument (a Collection) is error 450; an object that a default member returned and that has no value is error 13 (MS-VBAL §5.6.2.3); what the default member raises is raised; and a chain of default members that never returns a value that is not an object is error 28. MS-VBA raises the same for most of its functions (recorded in Excel), but not all: its Format raises 438 for an object a default member returned.

A String * n argument is its padded value, so Trim, RTrim, Left, Right and Mid see the padding.

A character member reads a String's characters, so a trailing byte is not one of them, as Len does not count it (RD-VBAL §2.4.1.2 VBStringType). A character is a UTF-16 code unit: Left, Mid and Right can split a surrogate pair. A byte member (AscB, ChrB, InStrB, LeftB, MidB, RightB) reads every byte, a trailing one included. StrComp compares as the relational operators do, so a trailing byte breaks a tie; Filter keeps an element as it was read; and StrConv keeps a trailing byte for a conversion of 0, and reads it for vbUnicode.

Compare and Option Compare

Filter, InStr, InStrB, InStrRev, Replace, Split and StrComp take a Compare, declared Optional Compare As VbCompareMethod = vbBinaryCompare. Of an omitted one MS-VBAL says "it uses the of the calling module".

The call site fills it in. The declarations mark Compare with StdLibOptionCompareAttribute, and a call that leaves it out passes vbTextCompare (1) where the calling module compares as Text and vbBinaryCompare (0) otherwise (RD-VBAL §5.3.1.11):

  • The calling module is the one whose code makes the call: a class module's code takes its own Option Compare, not that of the module that called it.
  • Option Compare Database is resolved through the environment, as the comparison operators resolve it, which makes it Text unless the environment says otherwise (RD-VBAL §5.2.1.1 Option Compare Directive). A member and = agree on what the calling module's mode is.
  • A caller with no calling module, such as a host that calls the library directly, gets the declared vbBinaryCompare.

A member reads Compare as it is given, and never consults Option Compare itself. An explicit Compare is vbBinaryCompare (0) or vbTextCompare (1); anything else (2, -1, a locale identifier) is error 5. VbCompareMethod has no vbDatabaseCompare.

Warning

🚧 MS-VBA takes a Compare that is a locale identifier - 3, 4, 100, 1031, 1033, not 5000, 32767 or 65535 - as a text comparison, Replace takes 2 as one where InStr and StrComp raise 5, and Danish and Norwegian do not expand æ (recorded in Excel). RD-VBA raises 5, as above.

In text mode two Strings are equal as = finds them under Option Compare Text (RD-VBAL §5.6.9.5 Relational Operators). A text-mode search does not use that comparison. MS-VBAL says nothing of how a text-mode search matches, and RD-VBA searches as MS-VBA does (recorded in Excel), the way Like matches (RD-VBAL §5.6.9.6): it searches the letters of both Strings, each character folded to one case, and æ, œ, ß and þ (and their capitals) expanded to the two letters they are. Nothing is ignored: a soft hyphen, vbNullChar, a full-width letter or katakana is a character of its own, so Split(buffer, vbNullChar) splits at the null characters, and "ab" is not found in "a", a soft hyphen and "b". Accents are not case, and ij, the ligatures ff to st and dž are not expanded.

Search What it finds Example
InStr, InStrRev, Filter The letters of the pattern anywhere in the letters of the String, part of an expanded character too, found at the character the match starts in. InStr(1, "xæx", "ex", vbTextCompare) is 2; InStr(1, "xaex", "æ", vbTextCompare) is 2.
Replace, Split The same, where the match starts and ends with a character: part of an expanded character is not replaced, nor split at. Replace("xæx", "a", "-", , , vbTextCompare) is "xæx"; Replace("xaex", "æ", "-", , , vbTextCompare) is "x-x".

In binary mode a search matches the same code units.

Replace and Split take their matches from left to right, each search resuming after the run it found, so their matches never overlap. InStrRev's may: InStrRev("aaa", "aa") is 2.

String-Returning Twins

MS-VBAL §6.1.2.11.1 gives seventeen functions a $ form that returns a String: Chr$, ChrB$, ChrW$, Format$, LCase$, Left$, LeftB$, LTrim$, Mid$, MidB$, Right$, RightB$, RTrim$, Space$, String$, Trim$ and UCase$. Each is "functionally identical" to its Variant twin, "except that the return type of the function is String rather than Variant": it is the twin's result Let-coerced to String, so where the twin returns Null the $ function raises error 94 (Left$(Null, 1); RD-VBAL §5.5.1.2.10).

A name written with a type-declaration character is bound by its identifier, through the same tiers as any simple name (MS-VBAL §5.6.10; RD-VBAL §5.6.10 Simple Name Expressions), so Left$ is looked up as Left:

  • Where Left binds a declaration of the procedure, the module, the project or a referenced library (a variable, a constant, a parameter, a procedure, another module's public Left), Left$ is that declaration, and the character is judged against its type. After Dim Left$, Left$ = "x" assigns the variable; a module's Function Trim is what Trim$ calls; and Left$ beside Dim Left As Long is VBC09335.
  • Where Left binds a member of the standard library, or nothing, Left$ is the library's member named Left$, if the library declares one.
  • Otherwise what the identifier binds stands. Len$ is Len, which returns a Variant, so it is VBC09335, and the module does not load (RD-VBAL §5.0.3 Semantic Analysis).

A member after a dot is looked up as it is written: VBA.Left$ is the library's Left$ whatever the module declares, and VBA.Strings.Left the library's Left.

Errors

Within one call the errors come in one order: the coercions of the arguments, in the order they are declared; then a Null operand, which makes the result Null; then the values' own checks, in the order they are declared, before any shortcut to a result. So Mid(Null, 0) is Null though a start of 0 is error 5, and Replace("", "a", "b", 0) is error 5 though a zero-length expression gives a zero-length string.

When Error
InStr or InStrB is given no String to search, or no pattern 449
A Variant argument has no form of the type the member needs: an Error, Missing, or an array other than a Byte() read as a String 13
A Variant argument, or an element of a SourceArray, is Nothing, or an object whose default member returns Nothing 91
A Variant argument is an object with no default member 438
A Variant argument is an object whose default member takes an argument, such as a Collection 450
A Variant argument is an object whose default member returns an object that has no value 13
A Variant argument the member needs as a number is beyond a Long 6
A date to format outside the range of dates 6
A date to format before the first day of the Hijri calendar, when the session's calendar is Hijri 5
A Variant argument the member needs as a number or a date, Format's Format, Join's or Split's Delimiter, or an element of a SourceArray is Null 94
A start below 1, InStrRev's Start of 0 or below -1, a negative Length or Number, Replace's Count below -1, Split's Limit below -1 5
An explicit Compare other than 0 or 1 5
A FirstDayOfWeek, FirstWeekOfYear, NamedFormat or tristate that is no value of its enumeration; a NumDigitsAfterDecimal below -1 5
Asc, AscB or AscW of a zero-length String; a code Chr, ChrW or String has no character for 5
MonthName of a Month other than 1 to 12; WeekdayName of a Weekday other than 1 to 7 5
ChrB of a code outside 0 to 255 6
A StrConv conversion it does not make (§6.1.2.11.1.37) 5
A SourceArray that is not a one-dimensional array 13
A result longer than a String can hold, from Space, String or a Format function 14
A $ function whose Variant twin returns Null 94

Against MS-VBA

RD-VBA follows MS-VBAL where it and MS-VBA's own documentation differ, and reads a sentence of MS-VBAL as an erratum where its literal reading contradicts the same page's declaration or table. None of this was checked against MS-VBA itself.

  • Parameters have MS-VBAL's names, which are not always the Office VBA reference's: Asc's is StringValue rather than String, InStr's are Arg1, Arg2 and Arg3 rather than Start, String1 and String2, and StrConv's locale is LocaleID rather than LCID. A call that names an argument by its Office name is error 448, "Named argument not found".
  • LTrim, RTrim and Trim remove spaces, as MS-VBAL says, and a space is U+0020 alone: an ideographic space (U+3000) stays, whatever the code page.
  • ChrW accepts -32768 to 65535. MS-VBAL bounds it at -32,767, read here as the bottom of an Integer: AscW of U+8000 is -32768, and ChrW(AscW(c)) is c for every code unit.
  • A position or count that MS-VBAL leaves unbounded is error 5 where it cannot mean anything: a start below 1, InStrRev's Start of 0 or below -1, a negative Length or Number, Replace's Count below -1 and Split's Limit below -1. That is the Mid statement's rule for the same arguments (RD-VBAL §5.4.3.5 Mid Statement), where clamping would invent a meaning. Split's Limit of 0 is an empty array.
  • The InStr, InStrRev, Replace and String pages are read through errata, given with the members (§6.1.2.11.1.14, §6.1.2.11.1.15, §6.1.2.11.1.29, §6.1.2.11.1.38).
  • Two parameters are declared otherwise than MS-VBAL's declaration lines: Join's and Filter's SourceArray (§6.1.2.11.1.16), and StrConv's LocaleID (§6.1.2.11.1.37).

6.1.2.11.1 Public Functions

Note

This section describes the implementation of MS-VBAL §6.1.2.11.1 Public Functions.

§ Member Declared members Notes
6.1.2.11.1.1 Asc / AscW Asc See §6.1.2.11.1.1.
6.1.2.11.1.2 AscB AscB See §6.1.2.11.1.1.
6.1.2.11.1.3 AscW AscW See §6.1.2.11.1.1.
6.1.2.11.1.4 Chr / Chr$ Chr, Chr$ See §6.1.2.11.1.4.
6.1.2.11.1.5 ChrB / ChrB$ ChrB, ChrB$ See §6.1.2.11.1.4.
6.1.2.11.1.6 ChrW / ChrW$ ChrW, ChrW$ See §6.1.2.11.1.4.
6.1.2.11.1.7 Filter Filter See §6.1.2.11.1.7.
6.1.2.11.1.8 Format Format See §6.1.2.11.1.8.
6.1.2.11.1.9 Format$ Format$ A twin.
6.1.2.11.1.10 FormatCurrency FormatCurrency See §6.1.2.11.1.10.
6.1.2.11.1.11 FormatDateTime FormatDateTime See §6.1.2.11.1.11.
6.1.2.11.1.12 FormatNumber FormatNumber See §6.1.2.11.1.10.
6.1.2.11.1.13 FormatPercent FormatPercent See §6.1.2.11.1.10.
6.1.2.11.1.14 InStr / InStrB InStr, InStrB See §6.1.2.11.1.14.
6.1.2.11.1.15 InStrRev InStrRev See §6.1.2.11.1.15.
6.1.2.11.1.16 Join Join See §6.1.2.11.1.16.
6.1.2.11.1.17 LCase LCase See §6.1.2.11.1.17.
6.1.2.11.1.18 LCase$ LCase$ A twin.
6.1.2.11.1.19 Left / LeftB Left, LeftB See §6.1.2.11.1.19.
6.1.2.11.1.20 Left$ Left$ A twin.
6.1.2.11.1.21 LeftB$ LeftB$ A twin.
6.1.2.11.1.22 Len / LenB Len, LenB See §6.1.2.11.1.22.
6.1.2.11.1.23 LTrim / RTrim / Trim LTrim, RTrim, Trim See §6.1.2.11.1.23.
6.1.2.11.1.24 LTrim$ / RTrim$ / Trim$ LTrim$, RTrim$, Trim$ Twins.
6.1.2.11.1.25 Mid / MidB Mid, MidB See §6.1.2.11.1.25.
6.1.2.11.1.26 Mid$ Mid$ A twin.
6.1.2.11.1.27 MidB$ MidB$ A twin.
6.1.2.11.1.28 MonthName MonthName See §6.1.2.11.1.28.
6.1.2.11.1.29 Replace Replace See §6.1.2.11.1.29.
6.1.2.11.1.30 Right / RightB Right, RightB See §6.1.2.11.1.19.
6.1.2.11.1.31 Right$ Right$ A twin.
6.1.2.11.1.32 RightB$ RightB$ A twin.
6.1.2.11.1.33 Space Space A negative Number is error 5; one longer than a String can hold is error 14.
6.1.2.11.1.34 Space$ Space$ A twin.
6.1.2.11.1.35 Split Split See §6.1.2.11.1.35.
6.1.2.11.1.36 StrComp StrComp See §6.1.2.11.1.36.
6.1.2.11.1.37 StrConv StrConv See §6.1.2.11.1.37.
6.1.2.11.1.38 String String See §6.1.2.11.1.38.
6.1.2.11.1.39 String$ String$ A twin.
6.1.2.11.1.40 StrReverse StrReverse Reverses the order of the code units.
6.1.2.11.1.41 UCase UCase See §6.1.2.11.1.17.
6.1.2.11.1.42 UCase$ UCase$ A twin.
6.1.2.11.1.43 WeekdayName WeekdayName See §6.1.2.11.1.43.

6.1.2.11.1.1 Asc / AscW

Note

This section describes the implementation of MS-VBAL §6.1.2.11.1.1 Asc / AscW.

Asc is the code of the first character: its ASCII code below 128, and above it its code in the environment's ANSI code page (RD-VBAL §2.3.1.2 Session Services), where a double-byte code is one negative Integer and a character the code page lacks is the code its encoder maps it to. AscW is the first code unit, as an Integer, so one above U+7FFF is negative. AscB is the first byte of the String's UTF-16LE data.

A zero-length String is error 5 for all three. A String of a single byte (ChrB(65)) has no first character: Asc and AscW of it are error 5.

6.1.2.11.1.4 Chr / Chr$

Note

This section describes the implementation of MS-VBAL §6.1.2.11.1.4 Chr / Chr$.

Chr of 0 to 127 is the ASCII character, and of 128 to 255 the character of the environment's ANSI code page. Any other code is error 5, except on a double-byte code page, where -32768 to 65535 are codes, as MS-VBAL allows "a character set with a larger code point range". There a code above 255 is a lead byte and a trail byte, and a code from 128 on is error 5 unless the code page reads its byte, or its two bytes, as one character it defines, since the result is "a single character": in code page 932, Chr(&H81) is a lone lead byte, Chr(&H4142) would be two characters, and Chr(&H8100) names none, while Chr(&HB1) is the half-width katakana it names. A single-byte code page names a character with every code from 128 to 255.

ChrW takes -32768 to 65535, a negative code standing for itself plus 65536; a lone surrogate is a character like any other. ChrB is a String of one byte (Len 0, LenB 1), and a code outside 0 to 255 is error 6.

6.1.2.11.1.7 Filter

Note

This section describes the implementation of MS-VBAL §6.1.2.11.1.7 Filter.

Filter keeps the elements that contain Match as InStr finds it, or with Include False those that do not. A zero-length element contains nothing, and a zero-length Match is in every other element. An element is kept as the String it was read as, a trailing byte included.

The result is a String() from 0: Filter builds the array itself and never reads Option Base, so under Option Base 1 its lower bound is still 0 (RD-VBAL §5.2.1.2 Option Base Directive). It is (0 To -1) when nothing is kept.

SourceArray is a Variant, as Join's is (§6.1.2.11.1.16).

6.1.2.11.1.8 Format

Note

This section describes the implementation of MS-VBAL §6.1.2.11.1.8 Format.

MS-VBAL gives Format its declaration and its calendar rules, and nothing of what a format expression is. RD-VBA reads format expressions as the Office VBA reference documents them, in the environment's culture (RD-VBAL §2.3.1.2 Environment.Culture): its decimal and group separators and digit groups, its currency symbol and patterns, its date and time separators, patterns, and day and month names.

Without a Format, or with a zero-length one, Expression is the String a Let-coercion writes, so Format(1.5) is CStr(1.5) and Format(Null) is Null. Format is read as a String, so a Null one is error 94; it is cut to its first 257 characters, as MS-VBA documents. Expression is read as itself: Null, Empty, a Boolean, a number, a Date or a String, and a Byte() as its String; anything else is the error a String parameter would raise.

A named format is read in any case. General Number is the number as a Let-coercion writes it; Currency is FormatCurrency with every argument left out; Fixed, Standard, Percent and Scientific are the user-defined 0.00, #,##0.00, 0.00% and 0.00E+00; Yes/No, True/False and On/Off are the second word for zero and the first for any other number. General Date, Long Date, Short Date and Long Time are the user-defined c, dddddd, ddddd and ttttt; Medium Date is dd-mmm-yy (15-Mar-24, English being the language of the host); Medium Time is hh:nn AMPM, and Short Time hh:nn.

Any other format is user-defined, of up to four sections separated by semicolons outside its literals ("ABC", and the character after a \). What a section formats is read from the characters it uses outside its literals: @, &, <, > or ! make it a string section; otherwise any date or time symbol (c, d, w, m, q, y, h, n, s, ttttt, AM/PM, A/P, AMPM) makes it a date section; otherwise a digit placeholder, a separator, % or an exponent makes it a number section. So a date or time letter is never a literal unless it is quoted or escaped: "0 h" is a date format, whose h is the hour.

  • A number section writes Expression's magnitude rounded half away from zero on its decimal digits: a whole number, a Currency and a Decimal exactly, a Single to 7 significant digits and a Double to 15, as a Let-coercion to String has them (RD-VBAL §5.5.1.2.4), so Format(1.005, "0.00") is 1.01 and Format(2.5, "0") is 3. Digits the placeholders have no room for are written at the first of them. A , between placeholders of the whole part groups its digits by the culture's digit groups; one that ends the whole part divides by 1000 (##0,, writes millions). A . is always written: Format(5, "#.##") is 5.. / and : are the culture's date and time separators.
  • A date section writes Expression Let-coerced to a Date, to the nearest second, in the session's calendar. Every symbol is read in either case. m and mm are minutes when the date or time symbol before them is h or hh, whatever separators are between, and months otherwise (mm:ss is the month). w and ww count by FirstDayOfWeek and FirstWeekOfYear, vbUseSystem being the culture's. AM/PM and A/P are written in the case the format has them; AMPM is the culture's designator. A run longer than a symbol's longest form is that form and the rest of the run.
  • A string section writes Expression's String, right-aligned in its placeholders unless it has a !; characters the placeholders have no room for are written all the same (Format("ABCDE", "@@") is ABCDE), and a section with no placeholder writes its literals and then the whole String.

When the first section is a string section, any value is written as its String, by the second section if that is zero-length or Null and the format has one. Otherwise a String that reads as a number is that number, one that reads as a date is that date, and any other is written as it is. The section is chosen by the sign before rounding: the second for a negative number and the third for zero when they are there and not empty, and otherwise the first, with a minus sign before everything it writes for a negative number, so Format(-0.001, "0.00") is -0.00. A date and a Boolean are the numbers they are. A Null is written by the fourth section, and is Null when there is none.

FirstDayOfWeek and FirstWeekOfYear are values of their enumerations, else error 5, checked after a Null Expression has made the result Null.

Under the Hijri calendar (RD-VBAL §6.1.2.4) the date symbols count in it, mmm is the full month name, and the names and the patterns of c, ddddd, dddddd and ttttt are the culture's own Hijri ones if it has them, and Arabic (Saudi Arabia)'s otherwise, with the culture's own date and time separators. Without a format, a date is still written as a Let-coercion writes it, in the Gregorian calendar.

6.1.2.11.1.10 FormatCurrency / FormatNumber / FormatPercent

The three round half away from zero to NumDigitsAfterDecimal places, and write the culture's separators and digit groups. FormatCurrency writes the culture's currency symbol where its currency patterns put it; FormatPercent writes the number times 100 followed by %, the sign or the parentheses around both (-50.00%, (50.00%)). A number that rounds to zero has no sign.

What the specification leaves to the implementation is decided so:

Left out, or vbUseDefault RD-VBA
NumDigitsAfterDecimal (-1) The culture's currency places for FormatCurrency; 2 for the other two, as Fixed and Standard have. .NET's places for numbers are not read: ICU and Windows disagree on them (3 and 2 for en-US).
IncludeLeadingDigit True
UseParensForNegativeNumbers Where the culture's negative pattern has parentheses
GroupDigits Where the culture has digit groups

vbTrue with a currency pattern that has no parentheses puts the amount and its symbol in parentheses, the symbol where the positive pattern has it; vbFalse with one that has them puts a minus sign before the amount and its symbol.

Expression is read as a Double, except that a number is read as the value it is, so a Currency, a Decimal or a LongLong keeps every digit; Empty is 0 and a Date its serial number. Null is error 94, and a String that is no number error 13. A NumDigitsAfterDecimal below -1, and a tristate other than vbTrue (-1), vbFalse (0) or vbUseDefault (-2), are error 5: the pages' tables give 1 and 2, read as errata of MS-VBAL §6.1.1.15's -1 and -2.

6.1.2.11.1.11 FormatDateTime

Note

This section describes the implementation of MS-VBAL §6.1.2.11.1.11 FormatDateTime.

FormatDateTime is Format's named date formats: vbGeneralDate is General Date, vbLongDate Long Date, vbShortDate Short Date, vbLongTime Long Time, and vbShortTime Short Time ("the 24-hour format (hh:mm)"), in the session's calendar. Expression is Let-coerced to a Date: Null is error 94, a value that is no date error 13, and one outside the range of dates error 6. A NamedFormat other than 0 to 4 is error 5.

6.1.2.11.1.14 InStr / InStrB

Note

This section describes the implementation of MS-VBAL §6.1.2.11.1.14 InStr / InStrB.

What the first argument is depends on whether a third arrives. A third argument left out, or a caller's Missing passed on as one, makes the call the two-argument form: the first String searched for the second from position 1. Any other third argument, an explicit Empty included, makes the first argument the start. A start left out before a third argument is 1, and a String to search or a pattern left out is error 449.

The start is read as a Long would be ("If start contains the data value Null, an error occurs": error 94), then the Strings; a Null String gives Null, before the start is checked. A start below 1 is error 5. A zero-length String to search gives 0, and a zero-length pattern is found at the start, up to one past the end: InStr(4, "abc", "") is 4.

The page's three-argument prose says "Arg1 is used as a string", and its table gives 0 for "Arg1 > Arg3". Both are read as errata: the start is a number, and that row is a start past the end of the String to search, plus one for a zero-length pattern.

InStrB is the same over the String's bytes, a trailing one included, and may match inside a character. In text mode it searches characters, from the one at or after the start's byte, and gives the byte position of the character it finds.

6.1.2.11.1.15 InStrRev

Note

This section describes the implementation of MS-VBAL §6.1.2.11.1.15 InStrRev.

InStrRev finds the last match that ends by Start, and matches may overlap. A zero-length pattern is found at Start. A Start past the end of StringCheck gives 0, and a Start of 0 or below -1 is error 5.

The page contradicts itself, and is read so:

  • Its table says an omitted Start is "the data value 1, which means that the search begins at the last character position", where its declaration says Optional Start As Long = -1. The declaration's -1 is what is declared.
  • Its table says an omitted Compare performs "a binary comparison", where its runtime semantics name the calling module's Option Compare, as every other comparing member's do. The runtime semantics are what is declared.
  • Its "Start > Len(StringMatch)" is read as Len(StringCheck).
  • Its table gives Null for a Null StringCheck or StringMatch, which its declaration, As String, makes error 94 at the call site.

6.1.2.11.1.16 Join

Note

This section describes the implementation of MS-VBAL §6.1.2.11.1.16 Join.

Join joins the elements' Strings in index order, from the array's own lower bound, separated by Delimiter, a space when it is left out. Delimiter is read as Split's is (§6.1.2.11.1.35), after the array is checked.

MS-VBAL declares SourceArray() As Variant, for Join and for Filter. RD-VBA declares a Variant: an array is Let-coerced only to an array of its own element type (RD-VBAL §5.5.1.2.7), so a Variant() parameter would refuse the String() that Split returns, and Join(Split(s)) would be error 13. The member checks the array itself, and raises what the declared parameter would: a SourceArray that is Null, Empty, not an array, an array not yet dimensioned or one of more than one dimension is error 13. Any one-dimensional array is joined, of any element type, fixed or resizable. Every element is read as a String before any is joined: Null is error 94; Nothing, an element of an Object() never assigned included, is error 91; an Error, Missing, an object that is not Nothing, or an array other than a Byte() is error 13; and an element of a Variant() never assigned is Empty, a zero-length string.

6.1.2.11.1.17 LCase

Note

This section describes the implementation of MS-VBAL §6.1.2.11.1.17 LCase.

LCase and UCase case by the environment's regional settings (a Turkish I and i case to the dotless and dotted forms), one character for one, so "ß" stays "ß". Null is Null.

6.1.2.11.1.19 Left / LeftB

Note

This section describes the implementation of MS-VBAL §6.1.2.11.1.19 Left / LeftB.

Left, Right, LeftB and RightB coerce the String, then answer a Null one with Null whatever the Length, then refuse a negative Length with error 5. The character forms count code units, and the byte forms bytes, so an odd Length gives a String that ends inside a character, or for RightB begins inside one.

6.1.2.11.1.22 Len / LenB

Note

This section describes the implementation of MS-VBAL §6.1.2.11.1.22 Len / LenB.

Strings.Len and Strings.LenB are the RD-VBA implementation of MS-VBAL §6.1.2.11.1.22. Both have a runtime implementation.

MS-VBAL says the pair returns "the number of characters in a string or the number of bytes required to store a variable on the current platform". It says LenB "will return the same value as Len, except for strings or UDTs": one function with two exceptions (strings and UDTs), rather than two functions.

Len and LenB are therefore implemented as one measurement, with one option that selects between them.

Expression Len LenB
A String Its number of whole characters. "the number of bytes used to represent that string": two per character, and one more for a trailing byte (RD-VBAL §2.4.1.2 VBStringType).
A fixed-length String Its declared length. Twice its declared length.
Null Null Null
Any other scalar The bytes it occupies. The same as Len: the bytes it occupies.
A UDT "the size as it will be written to the file": 👉 its members' memory widths, with no padding "the in-memory size, including any implementation-specific padding between elements"

UDT sizes

The UDT row is the one that needs the platform to know two different sizes for the same value. 🎯 A UDT has two sizes, and RD-VBA measures both as MS-VBA does (recorded in Excel, 64-bit), for the environment's pointer width:

  • LenB is its in-memory size: each member aligned to its own width (up to 8), and the whole padded to its widest member's.
  • Len is the same members' widths added up with no padding, a fixed-length String counted as its characters and a nested record or an array's elements as their own Len.

A variable-length String, an object, a LongPtr and a resizable array member are each a pointer (8 bytes in a 64-bit environment), a Variant is 24 (16 in a 32-bit one), and a fixed-size array member is its elements inline. So Len of { a As Long, s As String } is 12 whatever s holds, and its LenB is 16; { a As Byte, b As String * 3 } is 4 and 7.

👉 This is not "the size as it will be written to the file": a Put of a record writes a variable-length String member as its length and its characters. MS-VBAL warns that "Len might not be able to determine the actual number of storage bytes required when used with variable-length strings in user-defined data types"; MS-VBA, and RD-VBA, count the pointer.

RD-VBAL §2.5.2.1.3 User-Defined Type (UDT) Values holds the padding rule.

A string expression, or a variable

Expression is "any valid string expression, or any valid variable name", and RD-VBA checks it at compile time, as MS-VBA does (recorded in Excel). A String, a Variant (Now, CVar(x), Null, Empty), a late-bound object and a record are read as a value. A value of any other declared type must be a variable - a variable or a parameter, in parentheses or not, a field of a record, an element of an array - whose size is measured: Len(5), Len(1 + 2), Len(True), Len(FnLong()), Len(k) of a constant and Len(o.Value) of a property are VBC09333 Variable required.

An object stands for what its default member returns, which is no variable: Len of an object whose default member returns a String or a Variant counts its characters, and of one whose default member returns anything else - a Long, an object - is VBC09333. An object whose class has no default member is read when it runs.

Declared types at external dispatch

👉 An external call carries runtime values rather than typed ones, so the declared type of the value a member was called with is recovered at external dispatch (RD-VBAL §5.3.1.11 Procedure Invocation Argument Processing). The declared type survives external dispatch because each intrinsic stores its own exact managed type: a short for Integer and an int for Long, not one integer type for both (RD-VBAL §2.5.2.1.1 Numeric Values).

👉 This is what lets Len return "the number of bytes required to store a variable" for the variable it is given. Date and Double are indistinguishable at external dispatch. That costs Len nothing, because they have the same width; a parameter declared Date will need its declared type to recover its argument.

6.1.2.11.1.23 LTrim / RTrim / Trim

Note

This section describes the implementation of MS-VBAL §6.1.2.11.1.23 LTrim / RTrim / Trim.

LTrim, RTrim and Trim remove spaces, U+0020, and nothing else: a tab, a line break, a null character, a no-break space, an em space and an ideographic space stay. Null is Null.

6.1.2.11.1.25 Mid / MidB

Note

This section describes the implementation of MS-VBAL §6.1.2.11.1.25 Mid / MidB.

Length is a Variant, read as a Long would be after the String is coerced and before a Null String is answered, so Mid(Null, 0) is Null, and Mid of an Error value is error 13 whatever its Length. Then a Start below 1 is error 5, and so is a negative Length, past the end or not, as the Mid statement's rule is (RD-VBAL §5.4.3.5 Mid Statement). A Start past the end gives a zero-length string, and a Length left out, or a caller's Missing passed on, takes the rest. MidB is the same over the String's bytes.

6.1.2.11.1.28 MonthName

Note

This section describes the implementation of MS-VBAL §6.1.2.11.1.28 MonthName.

MonthName is the month's name in the environment's regional settings and the session's calendar, as Format's mmmm writes it, and with Abbreviate as its mmm writes it: under the Hijri calendar the 9th month is Ramadan, and a Hijri month name is not abbreviated. A Month other than 1 to 12 is error 5.

6.1.2.11.1.29 Replace

Note

This section describes the implementation of MS-VBAL §6.1.2.11.1.29 Replace.

Replace returns the String from Start on, with at most Count matches replaced, all of them when Count is -1. A Start below 1, a Count below -1 and an explicit Compare other than 0 or 1 are error 5, before anything else. A zero-length Expression, or a Start past its end, gives a zero-length string; a zero-length Find, or a Count of 0, gives the String from Start, unchanged. What a replacement puts in is not searched again.

The page contradicts itself, and is read so: its table gives a copy of Expression for a zero-length Find and for a Count of 0, where its prose says the result "begins at the position specified by Start" and "is not a copy of the original string from start to finish". The prose is what is read: Replace("abcabc", "", "X", 3) is "cabc".

6.1.2.11.1.35 Split

Note

This section describes the implementation of MS-VBAL §6.1.2.11.1.35 Split.

Split returns a String() from 0. It builds the array itself and never reads Option Base, so under Option Base 1 its lower bound is still 0 (RD-VBAL §5.2.1.2 Option Base Directive).

A zero-length Expression, or a Limit of 0, gives an empty array, (0 To -1). A zero-length Delimiter, or one not found, gives the whole String in one element. With a Limit of n, the n-th element holds the rest of the String, delimiters and all. Delimiter is a Variant, read as a String would be (Empty is a zero-length string, a number is written in the environment's regional settings, Null is error 94), before Limit is checked.

The result can be assigned to a String() variable (parts = Split(s)), and indexed where it is (Split(s)(1)).

6.1.2.11.1.36 StrComp

Note

This section describes the implementation of MS-VBAL §6.1.2.11.1.36 StrComp.

StrComp coerces both Strings, gives Null if either is Null whatever Compare is, then checks Compare, and then compares the two as the relational operators do, a trailing byte included, as an Integer -1, 0 or 1. So StrComp(a, b) is 0 exactly when a = b under the Option Compare that Compare names.

6.1.2.11.1.37 StrConv

Note

This section describes the implementation of MS-VBAL §6.1.2.11.1.37 StrConv.

Conversion is a sum of VbStrConv values. A value outside 0 to 255 is error 5, and so is one that names both of a pair MS-VBAL makes "mutually exclusive": vbUnicode and vbFromUnicode, vbWide and vbNarrow, vbKatakana and vbHiragana. As in MS-VBA, vbUnicode or vbFromUnicode with any other conversion is error 5 too.

  • vbUnicode reads the String's bytes, a trailing one included, as text in "the default code page of the system", the environment's ANSI code page. vbFromUnicode writes the characters as that code page's bytes: "abc" becomes a String of three bytes. A LocaleID does not change that code page.
  • Every other conversion reads the String in the ANSI code page of LocaleID's locale, and writes the result back from it, as MS-VBA does. Left out or 0, or naming a locale with no ANSI code page, it is the environment's code page. A character the code page lacks is the one its encoder maps it to, or ?, before any conversion sees it: in Windows-1252 α is a, ā is a, an ideographic or em space is a space, and γ is ?; in Windows-932 a no-break space is ?, Â¥ is \, and ã‚” is ヴ.
  • vbUpperCase and vbLowerCase case each character to the one Unicode gives, only when that one cases back to it, in any locale: the Turkish i is I, and ı, İ, µ and Ï‚ stay as they are. That is MS-VBA's casing (recorded in Excel), not the locale's.
  • vbProperCase lowercases the String, then uppercases its first character and each one after one of MS-VBAL's seven word separators (Chr$(0), Chr$(9) to Chr$(13), Chr$(32)), and no other: "o'neil-smith" is "O'neil-smith", and a no-break space starts no word. In an East Asian locale the ideographic space starts one, "the actual value for a space" varying "for DBCS" - as in MS-VBA (recorded in Excel, LocaleID &H411, &H804, &H404 and &H412). Elsewhere the code page has read it as a space, which starts one too.
  • vbWide writes each narrow character as its wide form and vbNarrow the other way round, the forms being Unicode's compatibility mappings: ASCII ! to ~ and the fullwidth forms, a space and the ideographic space, the halfwidth katakana, Japanese punctuation and Hangul and their wide forms, and ¢ £ ¬ ¯ ¦ Â¥ â‚© and theirs. MS-VBA leaves the backslash and the fullwidth backslash as they are, and so does RD-VBA. A halfwidth katakana and a halfwidth voiced or semi-voiced sound mark widen to one katakana (ガ is ガ), and a voiced katakana narrows to the two. A hiragana has no narrow form and stays wide.
  • vbKatakana writes each hiragana as its katakana, and vbHiragana each katakana as its hiragana; a katakana with no hiragana (ヷ to ヺ, the halfwidth ones) stays.
  • The conversions are made in one order: the case, vbWide, vbKatakana or vbHiragana, then vbNarrow. So a halfwidth katakana widened is a katakana for vbHiragana, and a hiragana made a katakana can be narrowed.
  • A Conversion of 0 gives the String as it was given, a trailing byte and all. Every other conversion reads its characters, so a trailing byte is dropped unless vbUnicode reads it. MS-VBAL's declaration makes LocaleID required, which would make StrConv(s, vbProperCase) error 449. RD-VBA declares it optional, with no default: left out, or 0, it is the environment's locale. A LocaleID that names no locale is error 5, and so are LOCALE_USER_DEFAULT and LOCALE_SYSTEM_DEFAULT (1024 and 2048).

vbWide, vbNarrow, vbKatakana and vbHiragana "cause run-time errors when used in locales where they do not apply", in LocaleID's locale or the environment's: vbWide and vbNarrow apply to "East Asia locales", read as the Chinese, Japanese and Korean ones, and vbKatakana and vbHiragana to "Japan only", read as the Japanese ones. Elsewhere they are error 5. MS-VBA does the same (recorded in Excel with LocaleID &H411, &H804, &H404, &H412, 1033 and &H40C).

6.1.2.11.1.38 String

Note

This section describes the implementation of MS-VBAL §6.1.2.11.1.38 String.

String's Character is a String, whose first code unit is repeated, or a code: any other value is read as a Long would be (Empty is 0, True is -1), taken Mod 256 above 255, and mapped as Chr maps it. A Null Character gives Null whatever the Number. A negative Number, a zero-length Character, a negative code and a code that names no character are error 5: on a double-byte code page, String(2, &H81) is a lone lead byte, as Chr(&H81) is (§6.1.2.11.1.4). A result longer than a String can hold is error 14. The page's table gives Null for a Null Number, which its declaration, As Long, makes error 94 at the call site.

6.1.2.11.1.43 WeekdayName

Note

This section describes the implementation of MS-VBAL §6.1.2.11.1.43 WeekdayName.

WeekdayName is the name, in the environment's regional settings, of the day Weekday days into a week that begins on FirstDayOfWeek, as Weekday counts them, so WeekdayName(Weekday(d)) is the name of d's day; with Abbreviate it is the abbreviated name. FirstDayOfWeek left out is the declared vbUseSystemDayOfWeek, the first day of the week of the regional settings: WeekdayName(1) is "lundi" in fr-FR. The page's table calls vbSunday the default, which its declaration does not: the declaration is what is declared, and MS-VBA's type library declares the same default, 0 (VBE7.DLL: Optional FirstDayOfWeek As VbDayOfWeek = 0). A Weekday other than 1 to 7, then a FirstDayOfWeek that is no value of its enumeration, are error 5.


⏮️ RD-VBAL §6.1.2.10 Math | ⏭️ RD-VBAL §6.1.2.12 SystemColorConstants