14. Working with text

Chapter 12 covered the everyday string tools: joining with .., length with #, upper and lower case, repeating, and formatting. This chapter adds the three for looking inside a string: slicing it, finding something in it, and replacing part of it.

Slicing with string.sub

string.sub(s, i, j) returns the part of s from position i to j, counting characters from 1:

print(string.sub("hello", 1, 3))   -- hel
print(string.sub("hello", 3))      -- llo   (from 3 to the end)
print(string.sub("hello", -2))     -- lo    (last 2 characters)

Three things to notice:

  • Positions start at 1, like list indexes (Chapter 22).
  • Leaving out j means "to the end of the string".
  • A negative position counts from the end: -1 is the last character, -2 the second-to-last, and so on.

Like every string function, sub returns a new string and leaves the original alone.

Open exercises/14/01-sub.lua. A variable holds a word. Using string.sub, print just its first letter, then just its last.

Searching with string.find

string.find(s, what) looks for what inside s. If it finds a match, it returns the start and end positions; if not, it returns nil:

print(string.find("hello world", "world"))   -- 7   11
print(string.find("hello world", "z"))        -- nil

Because a missing match is nil, find pairs well with if:

local sentence = "the password is swordfish"
if string.find(sentence, "sword") then
    print("Found it.")
else
    print("Not in there.")
end

find treats a few punctuation characters (. ( ) % + - * ? [ ] ^ $) as special "pattern" symbols, not literal text. Stick to plain letters and words for now and you will not hit this. To match one of those symbols literally, pass true as a fourth argument: string.find(s, ".", 1, true).

Replacing with string.gsub

string.gsub(s, what, replacement) returns a new string with every occurrence of what swapped for replacement, plus a count of how many swaps it made:

local shout, count = string.gsub("hello", "l", "L")
print(shout)   -- heLLo
print(count)   -- 2

If you only want the new string, ignore the count:

local clean = string.gsub("a-b-c", "-", " ")
print(clean)   -- a b c

The same pattern caveat from find applies to the what part: stick to plain text for now.

The colon shortcut

Every string.xxx(s, ...) call can also be written s:xxx(...), where the string before the colon becomes the first argument:

local name = "Keiko"
print(name:sub(1, 1))     -- K   (same as string.sub(name, 1, 1))
print(name:upper())       -- KEIKO

You will see this colon form everywhere in real Lua and Roblox code. You met it briefly in Chapter 12; here it is again, since string methods are where it shows up most.

Homework

Homework files are in exercises/14/homework/.

Problem 1 — Initials

Open exercises/14/homework/01-initials.lua. Two variables hold a first and last name. Using string.sub, print the person's initials, like K.R. for "Keiko Raharja".

Problem 2 — Contains

Open exercises/14/homework/02-contains.lua. A variable holds a sentence. Use string.find inside an if to print yes if it contains the word lua, and no if it does not.

Problem 3 — Censor

Open exercises/14/homework/03-censor.lua. A variable holds a short message. Use string.gsub to replace every space with a dash -, then print it.

Challenge — Last word length

Open exercises/14/homework/04-last-letter.lua. A variable holds a word. Using a negative position in string.sub, print its last three characters, then print its length with #. Make it work for any word without counting by hand.

Stuck or finished? Open the homework solutions page.