Erik's ToolBox Documentation
Index

Text Methods

Introduction

In addition to the built-in string functions (such as StrLen, CopyStr, and StrPos), ToolBox supports instance methods that you call directly on text variables. These methods use dot notation (e.g., MyText.ToLower()) and provide a modern, readable syntax for string manipulation.

This page documents all text instance methods available in ToolBox. Each entry includes the syntax, description, return value, and a practical example.

Where Text Methods Can Be Called

The interpreter hands text methods to the native Business Central Text implementation, so they behave exactly like standard AL. Two rules decide where you can write them:

  • The receiver must be a Text variable (a global, a local, or a procedure parameter), or

the Text result of a previous call in a dotted chain, such as Obj.GetText('name').ToUpper().

  • Positions are 1-based. IndexOf returns the 1-based position of the match and 0 when

nothing is found. Substring, Remove, IndexOf, and LastIndexOf all count from 1, exactly like CopyStr and StrPos.

The following receivers are not supported. Copy the value into a Text variable first.

Not supported Write instead
A string literal: 'a,b,c'.Split(',') Csv := 'a,b,c'; Parts := Csv.Split(',');
A Code variable: ItemNo.ToLower() Txt := ItemNo; Txt := Txt.ToLower();
A record field: Customer.Name.ToUpper() Name := Customer.Name; Name := Name.ToUpper();

Differs from standard AL: Standard AL allows text methods on Code values and on record fields. ToolBox requires a Text variable (or chained Text result) as the receiver.

Case Conversion

ToLower

text.ToLower(): Text

Returns a new text value with all characters converted to lowercase. The original variable is not modified.

Returns: Text - The lowercase version of the string.

var
    Input: Text;
    Result: Text;
begin
    Input := 'Hello World';
    Result := Input.ToLower();
    Message(Result); // hello world
end;

ToUpper

text.ToUpper(): Text

Returns a new text value with all characters converted to uppercase. The original variable is not modified.

Returns: Text - The uppercase version of the string.

var
    Input: Text;
    Result: Text;
begin
    Input := 'Hello World';
    Result := Input.ToUpper();
    Message(Result); // HELLO WORLD
end;

Search Methods

Contains

text.Contains(substring: Text): Boolean

Checks whether the text contains the specified substring. The comparison is case-sensitive.

Returns: Boolean - true if the substring is found, false otherwise.

Parameter Type Description
substring Text The text to search for
var
    Email: Text;
begin
    Email := 'user@example.com';
    if Email.Contains('@') then
        Message('Valid email format')
    else
        Message('Missing @ symbol');
end;

IndexOf

text.IndexOf(substring: Text[, startIndex: Integer]): Integer

Returns the 1-based position of the first occurrence of the specified substring. Returns 0 if the substring is not found. When startIndex is given, the search starts at that position.

Returns: Integer - The 1-based position, or 0 if not found.

Parameter Type Description
substring Text The text to search for
startIndex Integer Optional. 1-based position to start searching
var
    FullPath: Text;
    DotPos: Integer;
begin
    FullPath := 'document.xlsx';
    DotPos := FullPath.IndexOf('.');
    if DotPos > 0 then
        Message('Extension starts at position: %1', DotPos + 1); // 10
end;

IndexOfAny

text.IndexOfAny(anyOf: Text[, startIndex: Integer]): Integer

Returns the 1-based position of the first character in the text that appears anywhere in anyOf. Returns 0 if none of the characters is found.

Returns: Integer - The 1-based position, or 0 if not found.

Parameter Type Description
anyOf Text The set of characters to look for
startIndex Integer Optional. 1-based position to start searching
var
    Line: Text;
    SepPos: Integer;
begin
    Line := 'Name;City,Country';
    SepPos := Line.IndexOfAny(',;');
    Message('First separator at %1', SepPos); // 5
end;

LastIndexOf

text.LastIndexOf(substring: Text[, startIndex: Integer]): Integer

Returns the 1-based position of the last occurrence of the specified substring. Returns 0 if the substring is not found.

Returns: Integer - The 1-based position of the last occurrence, or 0 if not found.

Parameter Type Description
substring Text The text to search for
startIndex Integer Optional. 1-based position to search backwards from
var
    FilePath: Text;
    LastSlash: Integer;
begin
    FilePath := 'C:\Users\Documents\report.pdf';
    LastSlash := FilePath.LastIndexOf('\');
    if LastSlash > 0 then
        Message('File name: %1', FilePath.Substring(LastSlash + 1)); // report.pdf
end;

StartsWith

text.StartsWith(prefix: Text): Boolean

Checks whether the text begins with the specified prefix. The comparison is case-sensitive.

Returns: Boolean - true if the text starts with the prefix, false otherwise.

Parameter Type Description
prefix Text The prefix to check
var
    DocNo: Text;
begin
    DocNo := 'INV-2025-001';
    if DocNo.StartsWith('INV-') then
        Message('This is an invoice: %1', DocNo)
    else if DocNo.StartsWith('CRM-') then
        Message('This is a credit memo: %1', DocNo);
end;

EndsWith

text.EndsWith(suffix: Text): Boolean

Checks whether the text ends with the specified suffix. The comparison is case-sensitive.

Returns: Boolean - true if the text ends with the suffix, false otherwise.

Parameter Type Description
suffix Text The suffix to check
var
    FileName: Text;
begin
    FileName := 'export_data.csv';
    if FileName.EndsWith('.csv') then
        Message('CSV file detected')
    else if FileName.EndsWith('.xlsx') then
        Message('Excel file detected');
end;

Modification Methods

Replace

text.Replace(oldValue: Text, newValue: Text): Text

Returns a new text with all occurrences of oldValue replaced by newValue.

Returns: Text - The text with replacements applied.

Parameter Type Description
oldValue Text The text to find and replace
newValue Text The replacement text
var
    PhoneNo: Text;
begin
    PhoneNo := '(555) 123-4567';
    PhoneNo := PhoneNo.Replace('(', '');
    PhoneNo := PhoneNo.Replace(')', '');
    PhoneNo := PhoneNo.Replace(' ', '');
    PhoneNo := PhoneNo.Replace('-', '');
    Message('Cleaned phone: %1', PhoneNo); // 5551234567
end;

Trim

text.Trim(): Text

Returns a new text with leading and trailing whitespace removed.

Returns: Text - The trimmed text.

var
    Input: Text;
begin
    Input := '   Hello World   ';
    Message('[%1]', Input.Trim()); // [Hello World]
end;

TrimStart

text.TrimStart([chars: Text]): Text

Returns a new text with leading whitespace removed. When chars is given, every leading character that appears in chars is removed instead.

Returns: Text - The text with leading characters removed.

var
    Input: Text;
begin
    Input := '   Hello World   ';
    Message('[%1]', Input.TrimStart()); // [Hello World   ]

    Input := '000123';
    Message('%1', Input.TrimStart('0')); // 123
end;

TrimEnd

text.TrimEnd([chars: Text]): Text

Returns a new text with trailing whitespace removed. When chars is given, every trailing character that appears in chars is removed instead.

Returns: Text - The text with trailing characters removed.

var
    Input: Text;
begin
    Input := '   Hello World   ';
    Message('[%1]', Input.TrimEnd()); // [   Hello World]

    Input := 'C:\Temp\';
    Message('%1', Input.TrimEnd('\')); // C:\Temp
end;

PadLeft

text.PadLeft(totalLength: Integer[, padChar: Text]): Text

Pads the text on the left side to reach the specified total length. By default, spaces are used for padding.

Returns: Text - The padded text.

Parameter Type Description
totalLength Integer The desired total length of the result
padChar Text Optional. A one-character text to use for padding
var
    Number: Text;
begin
    Number := '42';
    Message('Padded: %1', Number.PadLeft(6, '0')); // 000042
end;

PadRight

text.PadRight(totalLength: Integer[, padChar: Text]): Text

Pads the text on the right side to reach the specified total length. By default, spaces are used for padding.

Returns: Text - The padded text.

Parameter Type Description
totalLength Integer The desired total length of the result
padChar Text Optional. A one-character text to use for padding
var
    Caption: Text;
begin
    Caption := 'Item';
    Message('[%1]', Caption.PadRight(10, '.')); // [Item......]
end;

Remove

text.Remove(startIndex: Integer[, count: Integer]): Text

Returns a new text with characters removed starting at the specified 1-based position. If count is not specified, all characters from startIndex to the end are removed.

Returns: Text - The text with characters removed.

Parameter Type Description
startIndex Integer The 1-based starting position
count Integer Optional. Number of characters to remove
var
    Input: Text;
begin
    Input := 'Hello Beautiful World';
    Message(Input.Remove(6, 10)); // Hello World
    Message(Input.Remove(6));     // Hello
end;

Substring

text.Substring(startIndex: Integer[, length: Integer]): Text

Extracts a portion of the text starting at the specified 1-based position. If length is not specified, returns everything from startIndex to the end.

Returns: Text - The extracted substring.

Parameter Type Description
startIndex Integer The 1-based starting position
length Integer Optional. Number of characters to extract
var
    Input: Text;
begin
    Input := 'Hello World';
    Message(Input.Substring(7));     // World
    Message(Input.Substring(1, 5));  // Hello
end;

Split

text.Split(separator1: Text[, separator2: Text, ...]): List of [Text]

Splits the text into a list of substrings on every occurrence of any of the separators. The result is a 1-based List of [Text] that you can iterate with foreach.

Returns: List of [Text] - The list of substrings.

Parameter Type Description
separator1.. Text One or more delimiters to split on
var
    CsvLine: Text;
    Parts: List of [Text];
    Part: Text;
begin
    CsvLine := 'John,Smith,john@example.com,New York';
    Parts := CsvLine.Split(',');

    foreach Part in Parts do
        Message('Value: %1', Part);

    Message('Second value: %1', Parts.Get(2)); // Smith
end;

Building Longer Text

There is no TextBuilder type in ToolBox. Build text with the + operator or +=, which the interpreter handles efficiently for script-sized output. To insert a line break, put the character code 10 into a Text variable using string indexing:

var
    NL: Text;
    Report: Text;
begin
    NL[1] := 10;                       // a one-character text holding a line feed
    Report += 'Customer Report' + NL;
    Report += '===============' + NL;
    Message(Report);
end;

For collecting many pieces, a List of [Text] is a convenient buffer: add lines as you go and join them at the end.

Practical Examples

Cleaning Up Data

When importing or processing data, you often need to clean up text values by removing unwanted characters and normalizing whitespace.

var
    RawInput: Text;
    Cleaned: Text;
begin
    RawInput := '  John   Smith  ';
    Cleaned := RawInput.Trim();
    Message('Cleaned: [%1]', Cleaned); // [John   Smith]

    // Remove extra characters from a phone number
    RawInput := '+1 (555) 123-4567';
    Cleaned := RawInput.Replace('+', '');
    Cleaned := Cleaned.Replace('(', '');
    Cleaned := Cleaned.Replace(')', '');
    Cleaned := Cleaned.Replace(' ', '');
    Cleaned := Cleaned.Replace('-', '');
    Message('Phone digits: %1', Cleaned); // 15551234567
end;

Parsing Text

You can use a combination of text methods to parse structured text values. Remember that positions are 1-based.

var
    FullName: Text;
    SpacePos: Integer;
    FirstName: Text;
    LastName: Text;
begin
    FullName := 'Jane Doe';
    SpacePos := FullName.IndexOf(' ');
    if SpacePos > 0 then begin
        FirstName := FullName.Substring(1, SpacePos - 1);
        LastName := FullName.Substring(SpacePos + 1);
        Message('First: %1, Last: %2', FirstName, LastName);
    end;
end;

Parsing CSV Data

var
    CsvData: Text;
    Fields: List of [Text];
begin
    CsvData := 'Name,City,Amount';

    Fields := CsvData.Split(',');
    Message('Field 1: %1', Fields.Get(1));
    Message('Field 2: %1', Fields.Get(2));
    Message('Field 3: %1', Fields.Get(3));
end;

Building Output Text

When you need to build a report or export data as text, accumulate into a Text variable.

var
    Customer: Record Customer;
    Output: Text;
    NL: Text;
    LineCount: Integer;
begin
    NL[1] := 10;
    Output += 'Customer Report' + NL;
    Output += '================' + NL + NL;

    Customer.SetRange("Country/Region Code", 'US');
    if Customer.FindSet() then
        repeat
            LineCount += 1;
            Output += StrSubstNo('%1. %2 - %3 (%4)',
                LineCount,
                Customer."No.",
                Customer.Name,
                Customer.City) + NL;
        until Customer.Next() = 0;

    Output += NL + StrSubstNo('Total: %1 customers', LineCount);
    Message(Output);
end;

Case-Insensitive Comparisons

Text comparisons in AL are case-sensitive by default. You can use ToLower() or ToUpper() to perform case-insensitive comparisons.

var
    UserInput: Text;
    Expected: Text;
begin
    UserInput := 'Yes';
    Expected := 'yes';

    // Case-sensitive comparison fails
    if UserInput = Expected then
        Message('Match (case-sensitive)')
    else
        Message('No match (case-sensitive)');

    // Case-insensitive comparison succeeds
    if UserInput.ToLower() = Expected.ToLower() then
        Message('Match (case-insensitive)');
end;

Extracting File Extension

procedure GetFileExtension(FileName: Text): Text
var
    DotPos: Integer;
begin
    DotPos := FileName.LastIndexOf('.');
    if DotPos > 0 then
        exit(FileName.Substring(DotPos + 1).ToLower());
    exit('');
end;

begin
    Message('Extension: %1', GetFileExtension('Report.XLSX'));  // xlsx
    Message('Extension: %1', GetFileExtension('data.csv'));     // csv
    Message('Extension: %1', GetFileExtension('noextension'));  // (empty)
end;

Validating and Formatting Input

var
    Email: Text;
    AtPos: Integer;
    DotPos: Integer;
    Domain: Text;
begin
    Email := 'USER@Example.COM';

    // Normalize to lowercase
    Email := Email.Trim().ToLower();

    // Basic validation
    AtPos := Email.IndexOf('@');
    if AtPos <= 1 then
        Error('Invalid email: missing @ symbol');

    Domain := Email.Substring(AtPos + 1);
    DotPos := Domain.IndexOf('.');
    if DotPos <= 1 then
        Error('Invalid email: domain must contain a dot');

    Message('Validated email: %1', Email);
    Message('Domain: %1', Domain);
end;

Building Delimited Strings

procedure BuildCsvLine(Values: List of [Text]): Text
var
    Line: Text;
    Value: Text;
    IsFirst: Boolean;
begin
    IsFirst := true;
    foreach Value in Values do begin
        if not IsFirst then
            Line += ',';
        // Wrap in quotes if the value contains a comma
        if Value.Contains(',') then
            Line += '"' + Value + '"'
        else
            Line += Value;
        IsFirst := false;
    end;
    exit(Line);
end;

Working with Code and Field Values

Because text methods need a Text receiver, copy Code values and record fields into a Text variable before calling methods on them.

var
    Item: Record Item;
    Description: Text;
begin
    if Item.FindSet() then
        repeat
            Description := Item.Description;
            if Description.ToLower().Contains('bike') then
                Message('%1: %2', Item."No.", Item.Description);
        until Item.Next() = 0;
end;

Summary

Text methods in ToolBox give you a rich set of tools for searching, modifying, and analyzing string data. They follow the native AL rules: the receiver is a Text variable, and all positions are 1-based. Combined with the built-in string functions documented in the Built-in Functions page, you have everything you need to handle text processing tasks in your scripts.