Skip to content

Strings

Lesson 3.3

A string is a piece of text. Lua lets you join, cut, search and format strings with a small set of tools.

Writing strings

Put text between double quotes or single quotes. Both work the same; pick one and stick to it.

local block = "Oak Planks"
local mob = 'Creeper'
print(block, mob)  --> Oak Planks	Creeper

Inside a string, a backslash starts an escape sequence:

Escape Means
\n new line
\t tab
\" and \' a quote
\\ a backslash
\u{2764} the Unicode character with that code (here a heart)
print("Line one\nLine two")
print("She said \"hi\"")  --> She said "hi"

For text over several lines, use long brackets [[ ... ]]. Escapes don’t work inside them, which also makes them good for text full of backslashes:

local help = [[
/home  go home
/spawn go to spawn]]
print(help)
/home  go home
/spawn go to spawn

A line break right after the opening [[ is skipped.

Joining strings

The .. operator joins (concatenates) two strings. Numbers are converted for you:

local name = "Alex"
local level = 12
print(name .. " is level " .. level)  --> Alex is level 12

Lua does not use + for strings. "a" + "b" is an error, because + tries to turn both sides into numbers.

nil can't be joined

"Hello " .. nil is an error: attempt to concatenate a nil value. When a value might be missing, wrap it in tostring(...) first, which turns nil into the text "nil".

Length

The # operator gives the length of a string in bytes:

print(#"Torch")  --> 5

For plain English text, one character is one byte. Accented letters and other non-English characters take more than one byte. Use utf8.len to count characters:

print(#"Café")         --> 5
print(utf8.len("Café")) --> 4

Player names and chat can contain any characters, so use utf8.len when you show or limit text length.

Strings never change

You can’t change a string in place. Every string function returns a new string, and the original stays the same:

local s = "stone"
local loud = s:upper()
print(s, loud)  --> stone	STONE

The string library

Every string function lives in the string table, and you can call it two ways. These lines do the same:

print(string.upper("gold"))  --> GOLD
print(("gold"):upper())      --> GOLD

The second form, with a colon, is called method syntax. The lesson Classes and inheritance explains how the colon works. With a variable it reads nicely: name:upper().

The most used functions:

local s = "Diamond Sword"

print(s:upper())          --> DIAMOND SWORD
print(s:lower())          --> diamond sword
print(s:len())            --> 13
print(s:sub(1, 7))        --> Diamond
print(s:sub(9))           --> Sword
print(s:sub(-5))          --> Sword
print(s:rep(2, ", "))     --> Diamond Sword, Diamond Sword
print(s:reverse())        --> drowS dnomaiD
print(s:find("Sword"))    --> 9	13
  • sub(i, j) cuts out the characters from position i to j. Positions start at 1, not 0. Negative positions count from the end: -1 is the last character.
  • find gives the start and end position of the first match, or nil if there is none.
  • rep(n, sep) repeats the string n times, with an optional separator.

Two more for single bytes: string.byte("A") is 65, and string.char(72, 105) is "Hi".

find, match, gmatch and gsub can also search with patterns, a small search language. The lesson String patterns covers them. Until then, two simple uses:

print(("hello world"):gsub("o", "0"))    --> hell0 w0rld	2
print(("a.b.c"):find(".", 1, true))      --> 2	2

gsub replaces every match and also returns how many it replaced. In find, passing true as the fourth argument turns pattern features off, so . means a real dot.

Formatting

string.format builds a string from a template. Each % code in the template is replaced by the next argument:

print(string.format("%s has %d diamonds", "Alex", 12))
--> Alex has 12 diamonds
print(string.format("Health: %.1f", 17.25))
--> Health: 17.2
print(string.format("Pos: %d, %d, %d", 100, 64, -30))
--> Pos: 100, 64, -30
Code Shows
%s any value as text
%d an integer
%f a float; %.2f means 2 decimals
%x an integer in hexadecimal
%5d, %-8s padded to 5 or 8 characters, - means left-aligned
%03d padded with zeros: 007
%% a real percent sign
%q a string in quotes, with escapes, safe to read back into Lua

%d needs a whole number. string.format("%d", 2.5) is an error, but 2.0 works.

Padding lines up columns:

print(string.format("%-10s %3d", "Stone", 64))
print(string.format("%-10s %3d", "Torch", 8))
Stone       64
Torch        8

Converting

tostring(value) turns any value into a string. tonumber(text) turns a string into a number, or nil if it isn’t one. Use tonumber on anything the player types:

local input = "16"
local amount = tonumber(input)
if amount then
  print("Giving " .. amount)  --> Giving 16
end

Try it

Given local item = " golden apple " (two spaces on each side), print it without the spaces at the start and end, with each word starting with a capital letter: Golden Apple. Use sub, upper, and find. Hint: find the space between the words.

Show a solution
local item = "  golden apple  "
local trimmed = item:sub(3, -3)          -- "golden apple"
local space = trimmed:find(" ")          -- 7
local first = trimmed:sub(1, space - 1)  -- "golden"
local second = trimmed:sub(space + 1)    -- "apple"

local function capital(word)
  return word:sub(1, 1):upper() .. word:sub(2)
end

print(capital(first) .. " " .. capital(second))  --> Golden Apple

This solution knows there are exactly two spaces on each side. The lesson String patterns shows a pattern that trims any amount of space in one line: item:match("^%s*(.-)%s*$").

Recap: strings are joined with .., measured with # (bytes) or utf8.len (characters), cut with sub, and built with string.format.