String Manipulation & Pattern Matching
Master Lua's string library and its own lightweight pattern-matching syntax — find, match, gmatch, gsub, and pattern classes and captures.
Introduction
Lua's string library covers everything from basic formatting to a surprisingly capable, if unconventional, pattern-matching system. In keeping with Lua's small-by-design philosophy, it does not implement full regular expressions — it has its own lighter, faster pattern syntax that covers the vast majority of real-world text processing needs.
- The core string library functions you'll use constantly.
- How Lua patterns differ from full regular expressions.
- How to find, match, capture, iterate, and substitute text with patterns.
The Core String Library
local s = "Hello, Lua!"
print(string.upper(s)) -- HELLO, LUA!print(string.lower(s)) -- hello, lua!print(string.len(s)) -- 12 (same as #s)print(string.sub(s, 1, 5)) -- Hello (1-based, inclusive)print(string.format("%s has %d characters", s, #s))Click Run to see what this code prints.
Since strings have a metatable pointing __index at the string library, s:upper() and string.upper(s) do exactly the same thing — the colon sugar from lesson 7 applies to Lua's built-in string type as well.
Lua Patterns Are Not Regex
Lua patterns look similar to regular expressions at a glance but are a distinct, simpler system — no alternation with |, no non-greedy quantifiers, and different escaping. What they lack in expressive power, they make up for in speed and in keeping Lua's core small.
| Pattern Class | Matches |
|---|---|
| %a | Any letter |
| %d | Any digit |
| %s | Any whitespace character |
| %w | Any alphanumeric character |
| %p | Any punctuation character |
| . (dot) | Any character at all |
| ^ and $ | Anchors: start and end of the string |
string.find and string.match
Use case: string.find() returns the start/end indices of the first match; string.match() returns the matched text itself (or captures, covered next) directly — match is what you reach for most often.
local text = "Order #4521 shipped"
local startIdx, endIdx = string.find(text, "%d+")print(startIdx, endIdx) -- 8 11
local orderNumber = string.match(text, "%d+")print(orderNumber) -- 4521Click Run to see what this code prints.
Captures
Wrapping part of a pattern in parentheses creates a capture, letting you pull out multiple specific pieces of a match at once.
local date = "2026-08-12"local year, month, day = string.match(date, "(%d+)-(%d+)-(%d+)")
print(year, month, day)Click Run to see what this code prints.
string.gmatch for Iteration
Use case: string.gmatch() returns an iterator function that produces every match in a string one at a time — the natural fit for a generic for loop, exactly like ipairs()/pairs() from lesson 5.
local sentence = "the quick brown fox"
for word in string.gmatch(sentence, "%a+") do print(word)endClick Run to see what this code prints.
string.gsub for Substitution
Use case: string.gsub() replaces every match of a pattern with a replacement string, and also returns the number of replacements made as a second value.
local text = "I like cats and cats like me"local result, count = string.gsub(text, "cats", "dogs")
print(result)print("Replacements made:", count)Click Run to see what this code prints.
Common Mistakes
- Writing a full regular expression (with | alternation or non-greedy ?) and expecting it to work — Lua patterns are a different, smaller system.
- Forgetting that pattern special characters (. % ( ) . + - * ? [ ] ^ $) need %-escaping when you want to match them literally.
- Ignoring the second value string.gsub() returns (the replacement count) when you actually need to know how many replacements happened.
Best Practices
- Reach for string.match() when you want the matched text or captures directly, and string.find() only when you specifically need index positions.
- Use string.gmatch() for iterating every match instead of manually looping with repeated string.find() calls.
- Test tricky patterns interactively in the Lua REPL (lesson 3) before dropping them into a larger script.
Frequently Asked Questions
Not with the built-in string library, but third-party LuaRocks libraries (like lrexlib) add full regex support if a project genuinely needs it — most text processing needs are covered by Lua patterns alone.
It keeps the standard library dramatically smaller and the matching engine faster, in line with Lua's core philosophy from lesson 1 — a deliberate tradeoff, not an oversight.
Escape it as %% — the same way you'd escape any of Lua's other pattern special characters when you want them treated literally.
Key Takeaways
- Lua's string library covers formatting, case conversion, substrings, and length out of the box.
- Lua patterns are a smaller, faster, distinct system from full regular expressions.
- string.match() extracts matched text and captures; string.find() returns index positions.
- string.gmatch() iterates every match; string.gsub() replaces matches and reports how many it replaced.
Summary
Lua's pattern system covers the vast majority of real-world text processing with a much smaller engine than full regex. Next, you'll cover modules and error handling — how to organize Lua code across files and handle failures gracefully.