Lua for Beginners: String Creation, .., # Length & string.upper/lower | Episode 4
Video: Lua for Beginners: String Creation, .., # Length & string.upper/lower | Episode 4 by Taught by Celeste AI - AI Coding Coach
Lua Strings: Creation, Length, and Built-ins
Double or single quotes.
..for concatenation,#for length,string.upper/lowerfor case,[[ ]]for multi-line. Build a name-badge generator.
Strings in Lua are simple by design: immutable, byte-oriented, with a handful of essential operations covered by the string standard library. Today we cover the basics and build a small name-badge generator.
Two ways to quote
local first = "Alice"
local last = 'Smith'
Both produce identical strings. Pick the one that doesn't conflict with the content:
- Use
"..."if the string contains apostrophes ("don't"). - Use
'...'if the string contains double quotes ('she said "hi"').
Or escape with backslash: "don\'t" works but reads awkwardly.
Concatenation with ..
local first = "Alice"
local last = "Smith"
local full = first .. " " .. last
print(full) -- "Alice Smith"
Use .., not +. The space is just another string literal joined into the chain.
For many concatenations (10+), use table.concat:
local parts = {"a", "b", "c", "d"}
print(table.concat(parts, ", ")) -- "a, b, c, d"
.. allocates a new string every time; for big lists it's slow. table.concat is one allocation.
Length with #
print(#full) -- 11 (length of "Alice Smith")
print("Length: " .. #full)
The # operator returns the length in bytes, not characters. For ASCII text these are the same, but for UTF-8 text they're different — "é" is 2 bytes but 1 character.
For character-counting on UTF-8 strings, use utf8.len(s) from Lua 5.3+'s utf8 library.
Case conversion
print(string.upper(full)) -- "ALICE SMITH"
print(string.lower(full)) -- "alice smith"
string.upper and string.lower work on ASCII letters. They don't handle locale-specific cases (Turkish dotted/dotless I, German ß, etc.) — for that, use a Unicode library.
Multi-line strings with [[ ]]
local message = [[
This is a long string.
It can span multiple lines.
No need to escape "quotes" inside.
]]
print(message)
Double square brackets create a long string. Anything between them — including newlines and quotes — is preserved verbatim. No escapes needed.
If the content has ]], use a level: [==[ ... ]==]. The number of = signs lets you nest different levels safely.
A common quirk: [[ immediately followed by a newline skips that first newline. So:
local s = [[
hello]]
is "hello", not "\nhello". Saves you from awkward leading newlines.
string.rep — repeat a string
local border = string.rep("=", 30)
-- border is now "=============================="
Useful for ASCII art, dividers, padding.
The name-badge generator
Putting it all together:
local first = "Alice"
local last = "Smith"
local full = first .. " " .. last
print("--- Name Badge ---")
local border = string.rep("=", 30)
print(border)
print(" Name: " .. string.upper(full))
print(" Length: " .. #full .. " characters")
print(border)
Output:
--- Name Badge ---
==============================
Name: ALICE SMITH
Length: 11 characters
==============================
Five lines of Lua, a small but real-feeling tool.
Strings are immutable
local s = "Hello"
s = s .. " World" -- creates a new string, rebinds s
You can't modify a string in place. s = s .. " World" allocates a new string and updates s to point to it. The old "Hello" becomes garbage and gets collected.
This is fine for normal use, but watch out in tight loops:
-- BAD: O(n²) memory
local result = ""
for i = 1, 1000 do result = result .. i end
-- GOOD: O(n) memory
local parts = {}
for i = 1, 1000 do parts[#parts + 1] = tostring(i) end
local result = table.concat(parts)
The naive version reallocates the whole string every iteration.
string.sub for substrings
local s = "Hello, World"
print(string.sub(s, 1, 5)) -- "Hello" (first to fifth char)
print(string.sub(s, 8)) -- "World" (from eighth to end)
print(string.sub(s, -5)) -- "World" (last 5 chars)
Lua strings are 1-indexed (not 0-indexed like C/Python). string.sub(s, i, j) returns the substring from index i to j. Negative indices count from the end.
Method-style calls
print(string.upper(s))
print(s:upper()) -- same thing
s:upper() is sugar for string.upper(s) — pass s as the first argument. The colon syntax (covered in episode 30) makes string code look more object-oriented.
Common stumbles
Mixing .. with arithmetic. "sum: " .. 5 + 3 works (8 then "sum: 8"), but "sum: " .. -5 confuses some parsers. Add parentheses when in doubt.
Forgetting # is bytes, not characters. #"é" is 2. Use utf8.len for character count.
Concatenating in a loop. O(n²) memory blow-up. Use a table + table.concat.
Using string.upper for non-ASCII. Doesn't handle locale-specific cases. For full Unicode case-folding, reach for a library.
Forgetting strings are 1-indexed. string.sub(s, 0, 5) is treated as (1, 5) because Lua clamps 0 to 1. Be explicit.
What's next
Episode 5: booleans, nil, and comparisons. The "truthy gotcha" — why 0 and "" are truthy in Lua, unlike most other languages.
Recap
Strings: "..." or '...', multi-line [[ ... ]]. Concatenate with ..; for many strings, build a table and table.concat. #s for length (in bytes). string.upper/lower for case. string.sub(s, i, j) for substrings (1-indexed; negative indices from end). string.rep(s, n) for repetition. Strings are immutable — modifications return new strings.
Next episode: booleans and the truthy gotcha.