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 leavessunchanged, 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)ishipthatcode, noth. - 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.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…