With version 17 we got a proper RegEx module in business Central. In this video, I show a AL based RegEx workbench and explain the basics of RegEx.

In this video, Erik introduces regular expressions (RegEx) in AL for Business Central. He demonstrates a custom “RegEx Workbench” application that provides an interactive environment for testing regex patterns, walks through common pattern matching scenarios, and shares a curated reference library of useful regex patterns for Business Central developers.
The Famous Quote: “Now You Have Two Problems”
The title references an old programming joke — attributed to a Netscape developer — that goes something like: “I had a problem, so I decided to use regex. Now I have two problems.” RegEx has a reputation for being complicated and intimidating, but it’s also incredibly powerful. With the introduction of a proper RegEx module in Business Central (arriving around version 17.1), AL developers now have access to this tool natively.
Prior to this, there were some pseudo-regex capabilities through .NET wrapper objects, but they were unreliable — some functions worked, some didn’t — making them largely impractical. The new module, however, is genuinely useful.
The Inner Loop Philosophy
Erik emphasizes the importance of keeping the developer’s “inner loop” as tight as possible. The inner loop is the cycle of writing code, compiling, deploying, testing, and debugging. The faster this loop runs, the more efficient you are as a developer. If deployment takes forever or it’s complicated to see results, development becomes frustrating.
This philosophy drives Erik’s approach of creating “workbench” pages — interactive environments where you can experiment with a technology or pattern directly within Business Central. He’s done the same with JavaScript in previous videos, and here he applies it to regex.
The RegEx Workbench
The workbench is built as a Card page with an input field for test data, a pattern field for the regex expression, and a subpage that displays matches. Here’s the main page:
page 51100 "RegEx Workbench"
{
PageType = Card;
Caption = 'RegEx Workbench';
UsageCategory = Administration;
ApplicationArea = all;
layout
{
area(Content)
{
field(InputData; InputData)
{
Caption = 'Input';
ApplicationArea = all;
MultiLine = true;
}
field(Pattern; Pattern)
{
Caption = 'Pattern';
ApplicationArea = All;
trigger OnAssistEdit()
var
PatRec: Record "RegEx Pattern";
begin
if page.RunModal(Page::Patterns, PatRec) = action::LookupOK then
Pattern := PatRec.RegEx;
end;
}
part(Matchpart; "RegEx Matches")
{
ApplicationArea = all;
}
}
}
actions
{
area(Processing)
{
action(MatchAction)
{
caption = 'Match';
ApplicationArea = All;
Promoted = true;
PromotedCategory = Process;
PromotedIsBig = true;
PromotedOnly = true;
trigger OnAction()
var
RegEx: Codeunit Regex;
begin
RegEx.Match(InputData, Pattern, Matches);
CurrPage.Matchpart.Page.Fill(InputData, Matches);
end;
}
}
}
var
Matches: Record Matches;
InputData: Text;
Pattern: Text;
}
The core of the regex functionality is remarkably simple — it’s essentially two lines of code: call RegEx.Match() with your input data and pattern, then display the results. The Regex codeunit and the Matches record come from the System Application module provided by Microsoft.
Displaying Match Results
The matches are displayed in a ListPart page that uses a temporary Matches table. One notable detail is the GetData function, which extracts the actual matched text from the input string:
page 51101 "RegEx Matches"
{
PageType = ListPart;
SourceTable = Matches;
SourceTableTemporary = true;
Editable = false;
layout
{
area(Content)
{
repeater(rep)
{
field(Index; Rec.Index)
{
ApplicationArea = All;
}
field(MatchIndex; Rec.MatchIndex)
{
ApplicationArea = All;
}
field(Success; Rec.Success)
{
ApplicationArea = All;
}
field(Length; Rec.Length)
{
ApplicationArea = All;
}
field(Data; GetData(Rec))
{
ApplicationArea = all;
Caption = 'Match';
}
}
}
}
procedure GetData(m: Record Matches): Text
begin
exit(copystr(InputData, m.Index + 1, m.Length));
end;
procedure Fill(_InputData: Text; var m2: Record Matches)
begin
InputData := _InputData;
rec.deleteall();
if m2.findset() then
repeat
rec.copy(m2);
rec.insert();
until m2.next() = 0;
end;
var
InputData: Text;
}
A Word of Caution: Zero-Indexed Results
Erik flags an important gotcha: the Matches table returns zero-indexed positions, but Business Central strings are one-indexed. This is why the GetData function uses m.Index + 1 when calling CopyStr. Erik considers this a misstep from Microsoft — the regex module should ideally have been adapted to use one-based indexing to stay consistent with the rest of the AL language.
He also notes — somewhat tongue-in-cheek — that the table being named “Matches” (plural) violates long-standing NAV/Business Central naming conventions, where tables should use singular names and only pages can be plural. “If I had done a code review on that, I would have pointed those two things out.”
RegEx in Action: Pattern Matching Examples
Simple Text Matching
Starting with something basic — given the input text “this is a test where I purchased a banana for 10.25 on the 3rd of May 2020 for testing”, searching for the pattern test returns two matches: one at index 10 and one at index 46, both with length 4.
At this point, you might think “I could do the same thing with a simple string position function.” And you’d be right — but the key insight is that test is already a pattern. It’s a pattern that says: match the character ‘t’, followed by ‘e’, followed by ‘s’, followed by ‘t’. Regex just lets you express much more sophisticated patterns.
Digit Matching
Using \d{1,2} searches for sequences of one or two digits. This matches 10, 25, 3, and 20 (twice from 2020). Notice that you don’t get a separate match for the second “20” in “2020” because the matching engine has already consumed those characters.
Changing the quantifier to \d{4} (exactly four digits) returns only 2020.
Decimal Number Matching
Building a pattern for decimal numbers demonstrates how regex expressions grow in complexity. The pattern (\+|-)?[0-9]+(\.[0-9]*)? matches decimal numbers, including optional signs and decimal points. Note that the dot needs to be escaped with a backslash because . is a special character in regex (matching any single character).
Email Validation
One of the more complex patterns in the library validates email addresses:
([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})
This pattern can be used both to locate email addresses within a block of text and to validate that user input is likely a valid email address. These are the two primary use cases for regex: finding patterns in data and validating that data conforms to an expected format.
Date Matching
For dates, the pattern \d{1,2}[- /.]\w{3,3}[- /.]\d{1,4} matches formats like “3/May/2020” or “13-Feb-2021”. The square bracket notation [- /.] means “match any one of these separator characters,” allowing flexible date formatting.
The Pattern Reference Library
Erik built a comprehensive reference table directly into the workbench. The Pattern field on the workbench supports an AssistEdit that opens a list of regex patterns with descriptions, so you can browse and select common patterns rather than having to remember them all.
The pattern table is populated with regex syntax reference entries and practical patterns:
table 51100 "RegEx Pattern"
{
fields
{
field(1; RegEx; Text[250])
{
Caption = 'RegEx';
}
field(2; Description; Text[1000])
{
Caption = 'Description';
}
}
keys
{
key(PK; RegEx) { }
}
procedure create()
begin
InsertRec('\d', 'Matches a digit character. Equivalent to [0-9].');
InsertRec('\w', 'Matches any word character including underscore. Equivalent to "[A-Za-z0-9_]".');
InsertRec('.', 'Matches any single character except a newline character.');
InsertRec('+', 'Matches the preceding character one or more times. For example, "zo+" matches "zoo" but not "z".');
InsertRec('?', 'Matches the preceding character zero or one time. For example, "a?ve?" matches the "ve" in "never".');
InsertRec('*', 'Matches the preceding character zero or more times. For example, "zo*" matches either "z" or "zoo".');
// ... many more entries including:
InsertRec('(\+|-)?[0-9]+(\.[0-9]*)?', 'Matches a decimal number');
InsertRec('\d{1,2}[- /.]\w{3,3}[- /.]\d{1,4}', 'Matches a date dd/mm/yyyy');
InsertRec('\d{1,2}\-{0,1}\s{0,1}\d{2}\s{0,1}[NS]', 'Matches a valid latitude');
InsertRec('\d{1,3}\-{0,1}\s{0,1}\d{2}\s{0,1}[EW]', 'Matches a valid longitude');
// Email pattern
InsertRec('([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})',
'Matches a valid email address');
end;
}
The library includes syntax references (special characters, quantifiers, character classes, lookaheads, lookbehinds) as well as ready-to-use patterns for common validation scenarios like emails, dates, decimal numbers, and geographic coordinates.
External Resources
Erik recommends regex101.com as an excellent external tool for learning and testing regex patterns. It functions as a web-based workbench similar to the one built in the video, but with a built-in reference manual and real-time match highlighting. It’s a great companion for developing patterns before bringing them into your AL code.
Summary
Regular expressions are a powerful addition to the AL developer’s toolkit in Business Central. While regex patterns can look intimidating — especially complex ones like email validation — you don’t need to start there. A simple match is often all you need. The key takeaways:
- RegEx arrived in Business Central around version 17.1 as a proper first-class module in the System Application
- Two primary use cases: locating patterns within text and validating input data against expected formats
- Watch out for zero-based indexing in the Matches table — add 1 when using results with AL string functions
- Build a pattern reference library — keep a notebook of expressions that work for your scenarios
- Use interactive tools like regex101.com or the workbench shown here to develop and test patterns quickly
- Start simple and build complexity as needed — don’t be intimidated by the more advanced patterns
Whenever you find yourself writing complicated AL code to verify whether a string contains an email, extract a number, or parse a date, regex is likely the right tool. The workbench app is available on GitHub for you to experiment with and extend.