This first edition was written for Lua 5.0. While still largely relevant for later versions, there are some differences.
The fourth edition targets Lua 5.3 and is available at Amazon and other bookstores.
By buying the book, you also help to support the Lua project.

20.1 – Pattern-Matching Functions

The most powerful functions in the string library are string.find (string Find), string.gsub (Global Substitution), and string.gfind (Global Find). They all are based on patterns.

Unlike several other scripting languages, Lua does not use POSIX regular expressions (regexp) for pattern matching. The main reason for this is size: A typical implementation of POSIX regexp takes more than 4,000 lines of code. This is bigger than all Lua standard libraries together. In comparison, the implementation of pattern matching in Lua has less than 500 lines. Of course, the pattern matching in Lua cannot do all that a full POSIX implementation does. Nevertheless, pattern matching in Lua is a powerful tool and includes some features that are difficult to match with standard POSIX implementations.

The basic use of string.find is to search for a pattern inside a given string, called the subject string. The function returns the position where it found the pattern or nil if it could not find it. The simplest form of a pattern is a word, which matches only a copy of itself. For instance, the pattern 'hello' will search for the substring "hello" inside the subject string. When find finds its pattern, it returns two values: the index where the match begins and the index where the match ends.

    s = "hello world"
    i, j = string.find(s, "hello")
    print(i, j)                      --> 1    5
    print(string.sub(s, i, j))       --> hello
    print(string.find(s, "world"))   --> 7    11
    i, j = string.find(s, "l")
    print(i, j)                      --> 3    3
    print(string.find(s, "lll"))     --> nil
When a match succeeds, a string.sub of the values returned by string.find would return the part of the subject string that matched the pattern. (For simple patterns, this is the pattern itself.)

The string.find function has an optional third parameter: an index that tells where in the subject string to start the search. This parameter is useful when we want to process all the indices where a given pattern appears. We search for a new pattern repeatedly, each time starting after the position where we found the previous one. As an example, the following code makes a table with the positions of all newlines in a string:

    local t = {}                   -- table to store the indices
    local i = 0
    while true do
      i = string.find(s, "\n", i+1)    -- find 'next' newline
      if i == nil then break end
      table.insert(t, i)
We will see later a simpler way to write such loops, using the string.gfind iterator.

The string.gsub function has three parameters: a subject string, a pattern, and a replacement string. Its basic use is to substitute the replacement string for all occurrences of the pattern inside the subject string:

    s = string.gsub("Lua is cute", "cute", "great")
    print(s)         --> Lua is great
    s = string.gsub("all lii", "l", "x")
    print(s)         --> axx xii
    s = string.gsub("Lua is great", "perl", "tcl")
    print(s)         --> Lua is great
An optional fourth parameter limits the number of substitutions to be made:
    s = string.gsub("all lii", "l", "x", 1)
    print(s)          --> axl lii
    s = string.gsub("all lii", "l", "x", 2)
    print(s)          --> axx lii

The string.gsub function also returns as a second result the number of times it made the substitution. For instance, an easy way to count the number of spaces in a string is

    _, count = string.gsub(str, " ", " ")
(Remember, the _ is just a dummy variable name.)