Strings and Pattern Matching Basics

· 4 min read · Updated March 17, 2026 · beginner
strings patterns pattern-matching beginner

Strings are everywhere in programming—and Lua gives you powerful tools to work with them. Whether you’re parsing data, building URLs, or processing user input, understanding Lua’s string capabilities will save you countless hours.

Creating Strings

In Lua, strings can be created using single quotes, double quotes, or long brackets:

local single = 'Hello'
local double = "World"
local long = [[This is a
multi-line string]]

Note: Lua strings are immutable. Every operation that appears to modify a string actually creates a new one.

The String Library

Lua’s string library provides functions for manipulating and analyzing strings. Most functions live in the string table:

local text = "Hello, Lua!"

-- Get length
print(#text)              -- 11
print(string.len(text))  -- 11

-- Convert to uppercase/lowercase
print(string.upper("hi"))  -- HI
print(string.lower("HI")) -- hi

-- Get substring
print(string.sub(text, 1, 5))  -- Hello

-- Find substring
local start, finish = string.find(text, "Lua")
print(start, finish)  -- 8  10

String Concatenation

Combine strings using the .. operator:

local first = "Hello"
local second = "World"
local greeting = first .. " " .. second
print(greeting)  -- Hello World

Tip: For building strings in loops, use table.concat instead of .. to avoid O(n²) performance:

local parts = {"a", "b", "c"}
local result = table.concat(parts, "-")
print(result)  -- a-b-c

Pattern Matching Basics

Lua’s pattern matching is a lightweight alternative to full regex. It’s built into the string library and uses special characters called magic characters:

CharacterMeaning
.Any character
%aAny letter
%dAny digit
%sAny whitespace
%wAlphanumeric characters
%pPunctuation
%b()Balanced parentheses
^Start of string (in pattern)
$End of string (in pattern)

Character Classes

Character classes let you match types of characters:

local text = "abc123DEF"

-- Match all letters
print(string.match(text, "%a+"))  -- abc

-- Match all digits
print(string.match(text, "%d+"))  -- 123

-- Match uppercase letters
print(string.match(text, "%u+"))  -- DEF

Repetition Operators

Control how many times a pattern matches:

OperatorMeaning
*0 or more (greedy)
+1 or more (greedy)
-0 or more (non-greedy)
?0 or 1
local text = "123abc456"

print(string.match(text, "%d+"))   -- 123 (greedy - longest match)
print(string.match(text, "%d-"))  -- (empty - non-greedy)
print(string.match(text, "%a*"))  -- abc (0 or more)
print(string.match(text, "%a?")) -- a (0 or 1)

Captures

Extract specific parts of a match using parentheses:

local date = "2024-03-17"

-- Capture each component
local year, month, day = string.match(date, "(%d+)-(%d+)-(%d+)")
print(year, month, day)  -- 2024  03  17

-- Extract domain from email
local email = "user@example.com"
local user, domain = string.match(email, "(.+)@(.+)")
print(user, domain)  -- user  example.com

Common Pattern Operations

Finding and Replacing

local text = "The quick brown fox"

-- Find a pattern
local pos = string.find(text, "quick")
print(pos)  -- 5

-- Replace all occurrences (gsub)
local result = string.gsub(text, "o", "X")
print(result)  -- The quick brXwn fXx

-- Replace with captures
local result = string.gsub("Hello, John!", "(%w+)", "%1!")
print(result)  -- Hello!, John!!

Splitting Strings

Lua doesn’t have a built-in split function, but you can create one:

function split(str, delimiter)
    local result = {}
    local pattern = string.format("([^%s]+)", delimiter)
    
    for match in string.gmatch(str, pattern) do
        table.insert(result, match)
    end
    
    return result
end

local parts = split("apple,banana,cherry", ",")
print(table.concat(parts, ", "))  -- apple, banana, cherry

Validating Input

Pattern matching is excellent for validation:

function is_valid_email(email)
    -- Simple pattern: something@something.something
    return string.match(email, "[^@]+@[^@]+%.[^@]+") ~= nil
end

function is_phone_number(phone)
    -- Accepts: 123-456-7890 or (123) 456-7890
    return string.match(phone, "%(?%d% d%d%d%)?%s*%d%d%d-%d%d%d%d") ~= nil
end

print(is_valid_email("test@example.com"))     -- true
print(is_valid_email("invalid"))              -- false
print(is_phone_number("123-456-7890"))        -- true

Escaping Magic Characters

When you need to match a literal magic character, escape it with %:

local text = "Price: $99.99"

-- Match literal dollar sign
local price = string.match(text, "$%d+%.%d+")
print(price)  -- $99.99

-- Match literal period
local version = string.match("lua5.4", "5%.%d")
print(version)  -- 5.4

Practical Example: Parsing Log Lines

Let’s build a log parser that extracts useful information:

function parse_log_line(line)
    -- Pattern: [TIMESTAMP] LEVEL: Message
    local timestamp, level, message = string.match(
        line,
        "%[(%d+:%d+:%d+)%] (%w+): (.+)"
    )
    
    return {
        timestamp = timestamp,
        level = level,
        message = message
    }
end

local log = "[14:32:15] ERROR: Connection refused"
local parsed = parse_log_line(log)

print(parsed.level)    -- ERROR
print(parsed.message)  -- Connection refused

Summary

Lua’s string capabilities cover most common text-processing needs:

  • # operator and string.len for length
  • string.sub for extracting substrings
  • string.find for searching
  • string.gsub for replacements
  • Patterns for flexible matching and extraction
  • Captures to extract specific parts of matches

Pattern matching in Lua is simpler than regex but powerful enough for most tasks. Remember to escape magic characters (%, ., ^, $, etc.) when matching them literally.


Next in series: Modules and the require System

See Also

  • — Find substrings with string.find()
  • — Replace patterns with string.gsub()