String Patterns - What on earth are they?

What are String Patterns?

String patterns, is a specific arrangement or sequence of characters within a text or a string. For example, if you’re looking for all the numbers in a set of strings, you can create a string pattern that captures only the numbers in each string, (e.g “helloworld123”, pattern: “%d+”).

The Usage of String Patterns

  1. Search and Identify: You can create patterns to search for specific text within strings. For instance, you could use a string pattern to find all player names that start with “Ro”, allowing you to target or modify those players in your game.
  2. Filter and Validate: String patterns help you filter input or validate user data. For example, you might ensure that an input-based setting follows a specific pattern before allowing them to change the setting
  3. Formatting and Parsing: String patterns are useful for formatting text in a specific way or parsing data from strings. You could split a string into parts using a specific pattern, like breaking down a date string into day, month, and year.

The Basics

There are currently 3 string pattern methods you can utilize:

  1. string.match
    Looks for the first match in the string, returns the match
  2. string.gmatch
    Looks for all matches in the string, returns an iterator function
  3. string.gsub
    Looks for all matches in the string and replaces them accordingly, returns the replaced string
Class Represents
. Any character
%a An uppercase or lowercase letter
%l A lowercase letter
%u An uppercase letter
%d Any digit (number)
%p Any punctuation character
%w An alphanumeric character (either a letter or a number)
%s A space or whitespace character
%c A special control character
%x A hexadecimal character
%z The NULL character (\0)

Source: Strings | Documentation - Roblox Creator Hub
For single-letter character classes such as %a and %s, the corresponding uppercase letter represents the “opposite” of the class. For instance, %p represents a punctuation character while %P represents all characters except punctuation.

Character Usage
$ Search exclusively at the end of the string (e.g roblox$)
^ Search exclusively at the beginning of the string (e.g ^ilove)
( and ) Groups part of the pattern together, returns the capture separately (e.g (%d+)(%a))
[ and ] Defines a set of characters from which you want to match a class (e.g [%w%p])
+ A quantifier of one or more class
- A quantifier to match as few of the class possible, could also be used to define a range of characters (e.g [A-Z])
? A quantifier of 1 or less class
* A quantifier of 0 or more class

Applying the characters above to the same pattern:

  • %d+, matches with 01, 53, 159, 009, 1000
  • %d$, matches with abc1, helloworld9, 10, 0, was that a bite of 87
  • ^%d+, matches with 190k, 10m, 10kg, 87
  • (%d+)(%a+), matches with 100k, 100m, 100L, 100kg
  • [%d%p]+, matches with 100,000, 69,420, 500.00
  • %d?, matches with 1, 10, 59, 1000, 008
  • %d*, matches with abcdefg, hello1world, h3llo world, 109


%l = lowercase letters
%d= digits
%p = punctuations
. = any characters

String patterns may be unpredictable, and that’s fine! I had a lot of troubles memorizing and testing how would a pattern work out, and for that, i made a plugin that helps you test string patterns right from your studio environment

Plugin: https://web.roblox.com/library/13941603210/RegExplorer
Devforum Post: RegExplorer - a free tool to test string patterns

Scenarios

Here, i made a couple of scenarios to test your string pattern (and maybe problem solving) skills. If you found other solutions, let me know!

  1. Custom Commands
    You want to create a command handler that handles and parses the message into a data it can use. An admin can use commands such as /teleport Artzified, /kill Artzified. What pattern and methods should you use to achieve this?
Solution
local command, args = string.match(message, "/(%w+) (.+)") -- gets the command (%w+) and args string (.+)

local argsTable = {}
for argument in args:gmatch("%S") do -- gets all the arguments
	table.insert(argsTable, argument) -- inserts the arguments to the table
end

handleCommand(command, argsTable) -- handle the command

  1. NPC Dialogues
    Developing an NPC (non-playable character) system where dialogue responses are formatted based on the player name
local responses = {
"Greetings, {user}!",
"Hello there, {user}",
"Hi {user}, anything you'd like to discuss?"
}

How would you get a random response and format it accordingly?

Solution
local response = responses[math.random(#responses)] -- get a random response

response = response:gsub("{user}", player.Name) -- replace "{user}" with the player's name

  1. The Password Game
    You might’ve heard of the Password Game, which you need to apply each rule to your password, now we’re gonna sort of recreate it on a smaller scale. How do you make it so that the user has to have all numbers in their password to add up to 25?
Solution
local sum = 0 -- define the initial sum

for num in string.gmatch(password, "%d") do -- iterate over all digits in the password
	sum += num -- increase the sum by the single-digit number
end

local isValid = sum == 25 -- does sum add up to 25?

return isValid

  1. Form Validation
    You’re building a registration page for your, say very professional moderator. Users are required to put their email address for the form response. How do you make validate the email the user has inputted?
Solution
local match = string.match(email_address, "(%w+)@(%a+)%.com") -- (name)@(domain).com

return match ~= nil -- is it valid?

  1. Data Extraction
    You’re working on a data extraction project. You have a table containing URLs, and you need to extract all the domain names from the URLs.
local urls = {
	"roblox.com",
	"https://google.com",
	"www.wikipedia.com"
}

How do you achieve this?

Solution
local domains = {} -- store the domains

for url in urls do
	local domain = url:match("w?%.?(%w+)%..+") -- www(optional).(domain).(any)
	table.insert(domains, domain)
end

return domains

  1. Setting Input Validation
    You have a setting input where users can configure the setting to their preferences, but you haven’t implemented a check validation if the setting is a number, or a bunch of letters? How do you check if the user input is a number?
Solution

There are 2 working solutions in this scenario, one with a simple %d+, and the other utilizing the set - range pattern

local isNumber = string.match(input, "%d+") ~= nil

return isNumber
local isNumber = string.match(input, "[0-9]+") ~= nil

return isNumber

  1. Text Highlighting
    You want to make a text-highlighting system to highlight certain words within a string. For instance, the string is “When life gives you lemons”, and the highlight word is “lemon”, then the word “lemon” will be wrapped with <highlight> and ends with </highlight> (When life gives you <highlight>lemon</highlight>s). How do you do this?
Solution
local highlightedWord = "lemon"
local modifiedString = sample:gsub(highlightedWord, function()
	return `<highlight>{highlightedWord}</highlight>` -- returns the replacement text
end)

  1. A (simple) statement interpreter
    You have a state machine, in which you have a list of string statements that you have to evaluate.
    For example, you have the string: “advantage: 0; dist: >20”, which should translate to:
advantage == 0 and dist > 20

or you have the string: “!attack; dist: >12; advantage: >1”, which translates into:

not attack and dist > 12 and advantage > 1

and say, you also want to make a range function: “disadvantage: range(2, 4); dist:>7.5”, which should translate into:

disadvantage >= 2 and disadvantage <= 4 and dist > 7.5

and also it should support the < operator: “disadvantage: range(2, 4); dist:<7”

This is your code template to begin with:

local variables = {
	disadvantage = -advantage;
	advantage = advantage;
	attack = attacking;
	dist = dist;
}

local predicateString
local statements = predicateString:split(';')

local evaluation = {}
for _, statement in statements do

end

for _, eval in evaluation do -- all statements are using AND
	if eval == false then
		return false
	end
end
		
return true

Evaluation is a table of the statements evaluated, for instance; the predicate: “disadvantage: >5; dist:<7”; with disadvantage set to 6 and dist set to 8, should have an evaluation table of {true, false}

Solution
local variables = {
	disadvantage = -advantage;
	advantage = advantage;
	attack = attacking;
	dist = dist;
}

local predicateString
local statements = predicateString:split(';')

local evaluation = {}
for _, statement in statements do
    local negation, variable, operator, treshold = statement:match('(!?)(%l+):?([><]?)([%d%p]*)')
    local min_range, max_range = statement:match('range%([%d%.]+,[%d%.]+%)')

    local variable_value = variables[variable]

    assert(variable_value ~= nil, `Variable {variable} does not exists`)

    local statement_evaluation = false

    treshold = tonumber(treshold)

    if treshold then
        if operator == '>' then
            statement_evaluation = variable_value > treshold
        elseif operator == '<' then
            statement_evaluation = variable_value < treshold
        elseif operator == '' and treshold ~= '' then
            statement_evaluation = variable_value == treshold
        end
    end

    if min_range and max_range then
        min_range = tonumber(min_range)
        max_range = tonumber(max_range)

        statement_evaluation = variable_value >= min_range and variable_value <= max_range
    end

    if negation ~= '' then
        statement_evaluation = not statement_evaluation
    end

    table.insert(evaluation, statement_evaluation)
end

for _, eval in evaluation do
	if eval == false then -- all statements are using AND
		return false
	end
end

return true
26 Likes

very concise and cool and pro tutoral :sunglasses: :sunglasses: :sunglasses:

5 Likes

Thank you for this tutorial so much! I always faced problems with string patterns and well your here to save the day. Thank you hero!

2 Likes

It might be a little misleading to say that string patterns “could also be known as regex” because pattern matching is different from regexp. The former is more limited in its capacity against the latter. There are other languages that support real regex.

3 Likes

What, No.
Regex is something else, Although does the same purpose, It has way more features/freedom than string patterns, I recommend you do some research.

3 Likes

You’re wrong at this point

it looks like you should have done your own research

1 Like