Lua frontier pattern - undocumented feature
Posted: September 7th, 2022, 7:02 pm
When doing pattern matching on strings in Lua (equivalent to regex captures in Rainmeter), the Lua 5.1 reference manual has always been my final authority on what I can and can't do.
However, I just discovered an incredibly useful undocumented pattern available in Lua 5.1: the frontier pattern.
I was trying to match elements in a CSV (comma-separated value) string, but I encountered difficulty matching elements at the beginning and end of the string.
Example: Check if "1" is an element in "5,12,13" or "1,2,3"
This will return a false positive, because it will match the "1" in "12".
Ok, so let's match the delimiters to isolate the element.
This will fail because there is no comma before "1".
Can we match commas OR the beginning/end of a string? With regex, we could do something like: (?:,|^)(1)(?:,|$)
Alas, alternation is not available in Lua patterns, and even if it was, ^ and $ only match the beginning/end of strings if they are at the beginning/end of a pattern.
A basic solution is to bookend the CSV with commas to eliminate edge cases.
I could have stopped here, but I found the bookending inelegant. Then, I discovered the frontier pattern.
This detects the transition between characters not in a set to characters in a set. The [%d] following the %f tells the frontier pattern what is "in the set" (in this case, any numeric digit).
So, this solution requires no alteration of the input string.
The %D at the end is an inversion of %d, which means that any non-digit character is in the set, to detect the transition from number to non-number.
However, I just discovered an incredibly useful undocumented pattern available in Lua 5.1: the frontier pattern.
I was trying to match elements in a CSV (comma-separated value) string, but I encountered difficulty matching elements at the beginning and end of the string.
Example: Check if "1" is an element in "5,12,13" or "1,2,3"
Code: Select all
local csv = '5,12,13'
local element = '1'
local hasElement = csv:find(element) ~= nil
Ok, so let's match the delimiters to isolate the element.
Code: Select all
local csv = '1,2,3'
local element = '1'
local hasElement = csv:find(','..element..',') ~= nil
Can we match commas OR the beginning/end of a string? With regex, we could do something like: (?:,|^)(1)(?:,|$)
Alas, alternation is not available in Lua patterns, and even if it was, ^ and $ only match the beginning/end of strings if they are at the beginning/end of a pattern.
A basic solution is to bookend the CSV with commas to eliminate edge cases.
Code: Select all
local csv = '1,2,3'
local element = '1'
local hasElement = (','..csv..','):find(','..element..',') ~= nil
Code: Select all
%f[%d]
So, this solution requires no alteration of the input string.
Code: Select all
local csv = '1,2,3'
local element = '1'
local hasElement = csv:find('%f[%d]'..element..'%f[%D]') ~= nil