Skip to content
Lesson 4 of 18

Step 1 of 5 · Reading · ~3 min

Learn

Strings

String Basics

A Lua string is an immutable sequence of bytes. Nothing you call on a string ever changes it: every string function hands back a brand-new string. Internalise that one fact and a whole family of bugs disappears.

✗ result thrown away

local s = "hello"
s:upper()
print(s)

--> hello

✓ result kept

local s = "hello"
s = s:upper()
print(s)

--> HELLO

Building and measuring

.. joins strings; # measures one. Every .. also builds a new string, because the old ones cannot change.

local first = "Grace"
local last = "Hopper"
local full = first .. " " .. last
print(full)
print(#full)

--> Grace Hopper
--> 12

# counts bytes, not characters. For plain ASCII the two numbers agree. For anything else they do not, because Lua strings are byte strings and your source file is UTF-8.

print(#"cafe")
print(#"café")

--> 4
--> 5

When you genuinely need character counts, utf8.len(s) does it.

#s is an ordinary number, so it works in arithmetic and joins onto text like any other number. Mind the off-by-one: a string of 12 bytes with its first and last byte removed has 10 left, not 11.

local s = "shipthatcode"
print(#s - 4)
print("bytes: " .. #s)

--> 8
--> bytes: 12

Slicing with sub

Lua indexes strings from 1, and sub includes both endpoints. Negative positions count back from the end, with -1 meaning the last byte. With one argument, sub runs from that position to the end.

local s = "shipthatcode"
print(s:sub(1, 4))
print(s:sub(5, 8))
print(s:sub(9))
print(s:sub(-4))
print(s:sub(2, 2))

--> ship
--> that
--> code
--> code
--> h

Two habits to unlearn if you come from C, Python, Java or JavaScript: there is no index 0, and the second argument is the last position you want, not one past it. To take a single byte, give the same position twice.

Two ways to call the same function

string.upper(s) and s:upper() are the same call. The colon quietly passes the string as the first argument, so method style reads better when you chain. rep repeats a string.

print(string.upper("hi"))
print(("hi"):upper())
print(("ab"):rep(3))
print(string.find("shipthatcode", "that"))

Output (that last gap is a TAB, because find returns two values):

HI
HI
ababab
5	8

The parentheses in ("hi"):upper() are required; "hi":upper() will not parse.

How do I split a line into words?

Lua has no split function. The idiom is string.gmatch, which returns an iterator: a function you call again and again, getting the next match each time and nothing once there are none left. The pattern "%S+" means "one or more non-space bytes", so it yields each run of visible characters and skips the gaps between them.

local nextword = string.gmatch("to be  or not", "%S+")
print(nextword())
print(nextword())
print(nextword())

--> to
--> be
--> or

Normally a for loop makes those calls for you. The Loops lesson covers it, and this exercise does not need it.

Common mistakes

  • Discarding the result of upper: s:upper() on its own line leaves s unchanged, so you print the original case.
  • Using sub(2) for one byte: with a single argument it runs to the end of the string, so ("shipthatcode"):sub(2) is hipthatcode, not h.
  • Counting from 0: position 1 is the first byte, and sub(1, 3) returns three bytes.
  • Commas instead of ..: print("a", "b") puts a TAB between the values, so a value you meant to glue on comes out spaced.

Your exercise

Word Abbreviation reads one word and prints it in upper case, underlined with dashes, and shortened to a first letter, a count and a last letter. The starter reads the word; the three print calls are yours. The mistake the grader catches most is a count that is off by one.

Up nextString FormattingStrings

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…