0Pricing
Lua Academy · Lesson

String Basics: Concat and Length

Use .. for concatenation, # for length, and string.rep/reverse.

String Basics: Concat and Length is a free Lua Academy lesson on CoddyKit — lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Lua Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

String Concatenation with ..

Lua uses .. (two dots) for string concatenation. It works with strings and numbers (numbers are converted automatically). Unlike + in many languages, + in Lua is arithmetic only — using + on strings causes an error.

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

-- Numbers auto-converted
local age = 30
print("Age: " .. age)   -- Age: 30

-- Bad: print("x" + "y")  ERROR: arithmetic on string

Length Operator #

The # operator returns the byte length of a string. For ASCII strings, this equals the character count. For multi-byte UTF-8 strings, # counts bytes, not characters — a UTF-8 emoji counts as 4. Use a UTF-8 library for character-accurate lengths.

print(#"hello")        -- 5
print(#"")             -- 0
print(#"café")         -- 5 (c,a,f,é=2bytes)

local s = "Lua"
for i = 1, #s do
  io.write(string.byte(s, i) .. " ")
end
print()  -- 76 117 97

string.rep and string.reverse

string.rep(s, n, sep) repeats string s exactly n times with optional separator. string.reverse(s) reverses the byte order of the string. Both return new strings — Lua strings are immutable.

print(string.rep("ab", 3))       -- ababab
print(string.rep("ha", 3, "-"))  -- ha-ha-ha
print(string.rep("x", 0))        -- (empty)
print(string.reverse("hello"))   -- olleh
print(string.reverse("12345"))   -- 54321

string.upper and string.lower

string.upper(s) and string.lower(s) convert ASCII characters. They don't handle Unicode — non-ASCII characters pass through unchanged. For case-insensitive comparison, convert both strings to lower before comparing.

print(string.upper("hello"))     -- HELLO
print(string.lower("WORLD"))     -- world
print(string.upper("Lua 5.4!"))  -- LUA 5.4!

-- Case-insensitive comparison
local a = "Hello"
local b = "hello"
print(a:lower() == b:lower())    -- true

Colon Method Syntax

String functions can be called using colon syntax on string values: s:upper() instead of string.upper(s). This works because strings have a metatable with __index = string, so all string library functions are accessible as methods on any string.

local s = "  hello world  "
print(s:upper())         -- HELLO WORLD
print(s:len())           -- 15
print(s:rep(2, "|"))     -- same as string.rep(s,2,"|")

-- Chain calls
print(("lua"):upper():reverse())  -- AUL

String Immutability

Lua strings are immutable. You cannot modify characters in-place. Every string operation creates a new string. For heavy concatenation inside loops, use a table to accumulate parts and table.concat at the end — much more efficient than repeated ...

-- Efficient string building
local parts = {}
for i = 1, 5 do
  parts[i] = "item" .. i
end
local result = table.concat(parts, ", ")
print(result)  -- item1, item2, item3, item4, item5

-- Slow! Creates 5 intermediate strings:
-- local s = ""
-- for i = 1, 5 do s = s .. "item" .. i end

table.concat for Joining

table.concat(t, sep, i, j) joins array elements into a string with a separator. It only works with string and number values — tables or booleans cause an error. This is the fastest way to build strings from many parts.

local parts = {"one", "two", "three", "four"}
print(table.concat(parts, ", "))          -- one, two, three, four
print(table.concat(parts, " | ", 2, 3))  -- two | three

-- Building CSV
local row = {1, "Alice", 95, true}
-- Need tostring for non-string values:
for i, v in ipairs(row) do row[i] = tostring(v) end
print(table.concat(row, ","))

String Comparison

Strings are compared lexicographically by byte value. Uppercase letters come before lowercase in ASCII (A=65, a=97), so "Z" < "a" is true. Use string.lower on both sides for case-insensitive ordering.

print("abc" < "abd")    -- true
print("abc" < "abcd")   -- true (shorter)
print("Z" < "a")        -- true (Z=90, a=97)
print("10" < "9")       -- true (lexicographic!)
print(10 < 9)           -- false (numeric)

-- Sort strings case-insensitively
local words = {"Banana","apple","Cherry"}
table.sort(words, function(a,b) return a:lower()<b:lower() end)
print(table.concat(words,", "))

tostring and tonumber

tostring(v) converts any value to its string representation. tonumber(s, base) parses a string to a number. tonumber returns nil if parsing fails, unlike many languages that throw an exception. Base can be 2-36 for integer parsing.

print(tostring(42))        -- 42
print(tostring(true))      -- true
print(tostring(nil))       -- nil
print(tostring({1,2}))     -- table: 0x...

print(tonumber("3.14"))    -- 3.14
print(tonumber("0xff"))    -- 255 (hex)
print(tonumber("11",2))    -- 3 (binary)
print(tonumber("abc"))     -- nil (invalid)

String Interning

Lua interns short strings: identical string content shares the same object in memory. This makes == on strings effectively a pointer comparison after a hash check — very fast. Interning also means string creation has a small overhead (hash computation), but repeated strings are free.

local s1 = "hello"
local s2 = "hel" .. "lo"

-- Same content, interned to same object
print(s1 == s2)   -- true
print(#s1)        -- 5

-- Long strings may not be interned
-- They are still compared correctly by value
local long1 = string.rep("x", 1000)
local long2 = string.rep("x", 1000)
print(long1 == long2)  -- true

Quick Check

What is the most efficient way to build a long string from many small parts in Lua?

Recap: String Basics

Key points:

  • .. concatenates; never use + for strings
  • #s counts bytes (not Unicode chars)
  • s:upper(), s:lower(), s:rep(n, sep), s:reverse()
  • Strings are immutable — every op returns a new string
  • Build large strings with table + table.concat
  • tostring/tonumber for type conversion

Frequently asked questions

Is the “String Basics: Concat and Length” lesson free?

Yes — the full text of “String Basics: Concat and Length” is free to read here on the web, and the Lua Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Lua Academy course, upgrade to CoddyKit PRO.

What will I learn in “String Basics: Concat and Length”?

Use .. for concatenation, # for length, and string.rep/reverse. You practise Lua Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Lua Academy?

No prior experience is required. Lua Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “String Basics: Concat and Length” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Lua Academy lesson?

Yes. Every Lua Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. String Basics: Concat and Length
  2. Finding and Extracting Substrings
  3. string.format for Output
  4. string.gsub and string.gmatch
← Back to Lua Academy