A String variable consists of a sequence of 8-bit characters. There are two kinds of strings: variable-length (technically capable of storing 2 billion (231) characters but are generally limited to ±256 million (228)) and fixed-length strings (±1 million (220 - 1) characters).
Dim varname As String [ = string-literal] (variable-length)
Dim varname As String * len ( fixed-length)
Dim varname$
The codes for String characters range from 0-255. The first 128 characters (0-127) of the character set correspond to the letters and symbols on a standard U.S. keyboard. These first 128 characters are the same as those defined by the ASCII character set. The second 128 characters (128-255) represent special characters, such as letters in international alphabets, accents, currency symbols, and fractions and can differ depending on the value of Mode(Lang) and the current font face.
The type-declaration character for a variable length String is the dollar sign ($).
A variable-length string has a 4 byte pointer (descriptor) to a dynamically allocated memory block containing the string characters; the four bytes prior to this block store a 32-bit value containing the length of the string. For an empty string this pointer is null (0), meaning that no memory is allocated. Assigning data to a string will (re)allocate memory for the string data. Each string is terminated with a null character = Chr(0), but the terminating null is not counted as part of the string. The total size of the allocated string memory is 4 (length) + [the string itself] + 1 (#0). Given a string s$, the following is true:
String descriptor: ArrPtr(s$) or *s$
String memory: VarPtr(s$) or V: s$ (calculated from the descriptor: {*s$} )
Length: Len(s$) (using the descriptor length = {{*s$} - 4}, or using the address length = {V: s$ - 4} )
A fixed-length string is a piece of memory used to store a string and is the only string variable type allowed within a Type declaration (excepting strings in variants). This type of string does not have a descriptor, the variable directly addressing the memory allocated using the declaration statement String*n (fixed strings can not use the string literal ($)). The fixed-string is initialized with null characters; if a smaller string is assigned to a fixed-string, spaces will be added to the end of the string. However, if a larger string is assigned, only the characters which fit into the length of the string will be stored and any remaining characters will be lost.
A fixed string is not terminated with a null character (Chr(0)) and it has no length data field in front of it. The length of the fixed-string is inserted at compile time and not calculated at run time. The VarPtr and V: functions return the address of its memory location while the ArrPtr (or *) function returns the first four bytes of the fixed string; hence, these functions have no practical meaning for a fixed-string.
A constant string (string-literal) is a sequence of characters surrounded by quotation marks (").
Dim sName As String = "Basic"
Const BASICNAME As String = "GFA-BASIC"
To place a quotation mark (") inside a string constant, you must either place two quotes together or build a string using the character code for a quote (34) either using Chr(34) or #34:
sName = "A ""quoted"" constant." // sName now holds: A "quoted" constant
sName = "A " & Chr$(34) & "quoted" #34 " constant."
Strings stored in Variants are BSTR types in UNICODE or 'wide character' format. GFA-BASIC 32 takes care of allocating and converting the ANSI string to UNICODE by using a faster variant of the MultiByteToWideChar API function that maps a character string to a wide-character (Unicode) string. Some GFA-BASIC 32 string functions can be used on strings in Variants, providing support for UNICODE strings, although others, such as Len do not (use VStrLen instead).
A BSTR is more than a pointer to Unicode characters. The string length is maintained in a long variable just before the start address being pointed to, and the string always has an extra null character after the last character of the string. This null isn't part of the string, and you may have additional nulls embedded in the string. The BSTR data type is allocated using OLE Automation String functions, like SysAllocString.
The VarPtr and V: functions return the address of the Variant variable, not the string. To find the string data use VStrPtr.
Many OCX and Automation objects take a string value as a parameter. These strings are always BSTRs. When GFA-BASIC 32 comes to a point that it must pass or assign an ANSI string to a COM object, it converts the string to a BSTR first and passes the BSTR to the OLE property or method.
The reverse is true also. When a string returned from a COM object is assigned to a String data type, the BSTR is converted to ANSI using the internal GFA-BASIC 32 function mentioned above.
The way strings are passed to Windows API functions depends on how the external functions are declared. For the 1000 or so built-in (ANSI) API functions, the function arguments are not type checked at compile time. The compiler is only aware of the number of parameters and accepts 32-bits values only. Those API functions that expect a (pointer to a) string must be provided with the address of the GFA-BASIC 32 string using V:. For instance, the built-in CharLower function converts a character string or a single character to lowercase. The function takes only one parameter: a LPTSTR pointer to a null-terminated string. The string is converted in place, so that the return value is equal to the passed value. The following code does the job:
Dim s$ = "GFA-BASIC 32"
Debug V:s$, CharLower(V:s$), s$ // [address1] [address1] gfa-basic 32
What happens when s$ is passed, instead of its address? GFA-BASIC 32 pulls in one of 32 string buffers of 1030 bytes and copies the contents of s$ to the temporary buffer and passes the address of the buffer to the API function. The string is converted in place and thus the temporary buffer is modified and the memory location of the buffer is returned. s$ remains unchanged.
Dim s$ = "GFA-BASIC 32"
Debug V:s$, CharLower(s$), s$ // [address1] [address2] GFA-BASIC 32
By using Declare the API function can be introduced to GFA-BASIC 32 and force type checking on the parameters. A string parameter must always be declared using ByVal to get its address (V:) passed to the API function. A ByRef string parameter would obtain the address of the descriptor (ArrPtr).
Dim s$ = "GFA-BASIC 32"
Debug V:s$, CharLowerA(s$), s$
Declare Function CharLowerA Lib "user32" (ByVal lpsz As String) As Long
// [address1] [address1] gfa-basic 32
Variable-length Strings and Strings inside Variant variables are flexible and can be manipulated, lengthened or shortened; out of the two types, the GFA Variable-length strings are by far the faster, so the latter should only be used if you are using Unicode characters.
However, even GFA string manipulation can be slow at times, especially if carried out in quick repitition, as each time the length of the string is changed, a new memory block needs to be allocated, the new string read into it and the old memory block freed. Some tips for speeding up the process are:
// CREATING A NEW STRING BY REPEATEDLY ADDING NEW CHARACTERS
// Create a string of 100 characters (all "A")
Local a$ = String(100, "A"), ct% = 1, n%, t$, t# = Timer
// Method 1: Create a new string by adding the 100-character string 10,000 times
For n% = 1 To 10000 : t$ = t$ & a$ : Next n%
Trace Timer - t#
Trace Len(t$)
t# = Timer
// Method 2: Now, instead, create the new string by opening a temporary file... (usually 300 times faster)
Open App.Path & "\test.dat" for Output As # 1
// ...saving the 100-character string 10,000 times...
For n% = 1 To 10000 : BPut # 1, V:a$, Len(a$) : Next n%
// ...make sure that the buffer is cleared...
Flush # 1
// ...close the file...
Close # 1
// ...create space in the new string to take the data...
t$ = Space(FileLen(App.Path & "\test.dat"))
// ...and load in the data
BLoad App.Path & "\test.dat", V:t$
Trace Timer - t#
Trace Len(t$)
// EXTRACTING DATA FROM AN EXISTING STRING
t# = Timer
// Method 1: Use a position variable to mark the position of the data to be extracted
For n% = 1 To 10000 : a$ = Mid(t$, ct%, 100) : Add ct%, 100 : Next n%
Trace Timer - t#
t# = Timer
// Method 2: Extract the code and reduce the string (usually upto 2,000 times slower)
For n% = 1 To 10000 : a$ = Left(t$, 100) : t$ = Mid(t$, 101) : Next n%
Trace Timer - t#
If you need to delete certain sections and keep others, depending on how often you intend to shorten the main string, it may be better to use a counter on the main string and pass the sections you wish to keep to a new one using the file option in (1) above instead.
The case functions UCase and LCase are ASCII functions. The Upper and Lower functions convert the second 128 characters (128-255) also.
All string functions come in two versions, one with an ending $ type declaration character and one without. In contrast with VB, both version return a string data type. In VB the function without the $ character returns a Variant.
Very rarely, adding to a String that you have passed to a Function or Procedure ByRef will cause it to reduce in size to one character if it exceeds the length of the String that was initially passed. The following example does not replicate the error but shows a situation in which it can occur:
Global d$ = Space(50)
Test(d$)
Debug Len(d$) ` d$
Procedure Test(ByRef p$)
p$ = ""
Local n%
For n% = 1 To 100 : Test1(p$) : Next n%
EndProcedure
Procedure Test1(ByRef g$)
g$ = g$ & String(10, "A") & #13#10
EndProcedure
The workaround for this one is to use a local variable in Test and assign it to the passed string before exiting the procedure.[Reported by James Gaite, 15/07/2020]
Declare, Len, UCase, LCase, Upper, Lower, Variant, ArrPtr, VarPtr, V:, Left$, Right$, Mid, Mid$, SubStr, InStr, RinStr, Mirror, VstrLen, VStrLenB, VStrPtr
{Created by Sjouke Hamstra; Last updated: 17/02/2023 by James Gaite}