Language
日本語
English

Caution

JavaScript is disabled in your browser.
This site uses JavaScript for features such as search.
For the best experience, please enable JavaScript before browsing this site.

  1. Home
  2. Lua Dictionary
  3. string Library

string Library

The Lua string library provides functions for all common string operations, including length retrieval, substring extraction, case conversion, searching, substitution, formatting, and repetition. It is a standard library that requires no require call, and every function can be called either as a function (string.find(s, pat)) or as a method (s:find(pat)).

All Functions

FunctionDescription
string.len(s)Returns the number of bytes in string s.
string.sub(s, i [, j])Returns the substring of s from index i to j. When j is omitted, the substring extends to the end.
string.upper(s)Returns a copy of s with all ASCII alphabetic characters converted to uppercase.
string.lower(s)Returns a copy of s with all ASCII alphabetic characters converted to lowercase.
string.rep(s, n [, sep])Returns a string consisting of n copies of s. When sep is given, it is inserted between copies (Lua 5.2+).
string.reverse(s)Returns a string that is the byte-level reverse of s.
string.byte(s [, i [, j]])Returns the byte values of characters at positions i through j as multiple integers (default i=1, j=i).
string.char(...)Receives integer byte values and returns a string formed by concatenating the corresponding characters.
string.format(fmt, ...)Returns a formatted string according to the format string fmt (equivalent to C's printf).
string.find(s, pat [, init [, plain]])Searches string s for pattern pat and returns the start and end positions of the match plus any captures, or nil if not found.
string.match(s, pat [, init])Returns the first match (or captures) of pattern pat in string s, or nil if not found.
string.gmatch(s, pat)Returns an iterator function that yields each match of pattern pat in string s.
string.gsub(s, pat, repl [, n])Returns a copy of s in which all matches of pattern pat are replaced by repl (a string, table, or function), plus the total number of substitutions made.
string.dump(func [, strip])Returns a string containing the bytecode of Lua function func (for serialization).

Pattern Syntax

string.find(), string.match(), string.gmatch(), and string.gsub() all use Lua's own "pattern" syntax. It is similar to regular expressions but does not support | (alternation) or lookaheads.

Pattern ElementMeaning
.Matches any single character.
%aMatches any letter (A-Z, a-z).
%dMatches any digit (0-9).
%lMatches any lowercase letter.
%uMatches any uppercase letter.
%sMatches any whitespace character (space, tab, newline, etc.).
%wMatches any alphanumeric character (%a + %d).
%pMatches any punctuation character.
%cMatches any control character.
%xMatches any hexadecimal digit (0-9, A-F, a-f).
%A, %D, ... (uppercase)Matches the complement of the corresponding class (any character not in that class).
[set]Matches any character in the set (e.g. [aeiou], [0-9A-F]).
[^set]Matches any character not in the set.
*Matches 0 or more repetitions of the preceding element (longest match).
+Matches 1 or more repetitions of the preceding element (longest match).
?Matches 0 or 1 occurrence of the preceding element.
-Matches 0 or more repetitions of the preceding element (shortest match).
^Anchors the pattern to the start of the string (when placed at the start of the pattern).
$Anchors the pattern to the end of the string (when placed at the end of the pattern).
(pat)Capture group. Returns the matched portion as a separate return value.
%bxyMatches a balanced string beginning with x and ending with y (e.g. %b() matches balanced parentheses).
%1%9Back reference. Matches the same text as the n-th capture.
% (escape)Escapes a special character (e.g. %., %%).

Sample Code

psychopass_string.lua
-- psychopass_string.lua — sample for all string library functions
-- Uses PSYCHO-PASS characters to demonstrate each function

-- -----------------------------------------------
-- string.len — byte count
-- -----------------------------------------------

print("=== string.len ===")

local name_akane     = "Tsunemori Akane"
local name_shinya    = "Kogami Shinya"
local name_nobuchika = "Ginoza Nobuchika"
local name_shogo     = "Makishima Shogo"
local name_tomomi    = "Masaoka Tomomi"

print(string.format("%-22s  len=%d", name_akane,     string.len(name_akane)))
print(string.format("%-22s  len=%d", name_shinya,    string.len(name_shinya)))
print(string.format("%-22s  len=%d", name_nobuchika, string.len(name_nobuchika)))
print(string.format("%-22s  len=%d", name_shogo,     string.len(name_shogo)))
print(string.format("%-22s  len=%d", name_tomomi,    string.len(name_tomomi)))
-- Method notation yields the same result
print(string.format("Method notation: %s:len() = %d", name_akane, name_akane:len()))
print("")

-- -----------------------------------------------
-- string.sub — extract a substring
-- -----------------------------------------------

print("=== string.sub ===")

-- Split family name and given name from "Tsunemori Akane"
-- family name = bytes 1–9, given name = byte 11 to end
local family_akane = string.sub(name_akane, 1, 9)
local given_akane  = string.sub(name_akane, 11)
print(string.format("Family: %s  Given: %s", family_akane, given_akane))

-- Negative indices count from the end (-1 is the last byte)
local last5 = string.sub(name_shinya,    -5)
local last3 = string.sub(name_nobuchika, -3)
print(string.format("%s last 5 bytes: %s", name_shinya,    last5))
print(string.format("%s last 3 bytes: %s", name_nobuchika, last3))
print("")

-- -----------------------------------------------
-- string.upper / string.lower — case conversion
-- -----------------------------------------------

print("=== string.upper / string.lower ===")

local scan_label = "Psycho-Pass Level"

print(string.format("Original : %s", scan_label))
print(string.format("upper    : %s", string.upper(scan_label)))
print(string.format("lower    : %s", string.lower(scan_label)))

local labels = { "safe", "LATENT_CRIMINAL", "Clear", "ENFORCER" }
print("Normalized (upper):")
for _, label in ipairs(labels) do
    print(string.format("  %-20s -> %s", label, label:upper()))
end
print("")

-- -----------------------------------------------
-- string.rep — repeat a string
-- -----------------------------------------------

print("=== string.rep ===")

local gauge_fill = string.rep("*", 8)
local gauge_sep  = string.rep("O", 5, "-")
print(string.format("Gauge display : [%s]", gauge_fill))
print(string.format("With separator: %s",   gauge_sep))

local function danger_bar(level)
    return "[" .. string.rep("#", level) .. string.rep(".", 5 - level) .. "]"
end

local officers = {
    { name = "Tsunemori Akane",   level = 1 },
    { name = "Ginoza Nobuchika",  level = 2 },
    { name = "Kogami Shinya",     level = 4 },
    { name = "Makishima Shogo",   level = 5 },
    { name = "Masaoka Tomomi",    level = 3 },
}

print("Crime coefficient danger bar:")
for _, officer in ipairs(officers) do
    print(string.format("  %-22s %s Lv%d",
        officer.name, danger_bar(officer.level), officer.level))
end
print("")

-- -----------------------------------------------
-- string.reverse — reverse a string
-- -----------------------------------------------

print("=== string.reverse ===")

local serials = {
    "DOM-001-AKANE",
    "DOM-002-SHINYA",
    "DOM-003-NOBUCHIKA",
}

for _, serial in ipairs(serials) do
    print(string.format("  %-24s -> %s", serial, string.reverse(serial)))
end
print("")

-- -----------------------------------------------
-- string.byte / string.char — byte value conversion
-- -----------------------------------------------

print("=== string.byte / string.char ===")

local code_a = string.byte("Akane")
local code_s = string.byte("Shinya", 1)
local b2, b3, b4 = string.byte("Kogami", 2, 4)

print(string.format("byte('Akane')        = %d  (char '%s')",
    code_a, string.char(code_a)))
print(string.format("byte('Shinya', 1)    = %d  (char '%s')",
    code_s, string.char(code_s)))
print(string.format("byte('Kogami', 2,4)  = %d, %d, %d  (string '%s')",
    b2, b3, b4, string.char(b2, b3, b4)))

-- ASCII 72=H, 83=S, 66=B (initials of Hyper Sonic Bullet)
local acronym = string.char(72, 83, 66)
print(string.format("string.char(72,83,66) = '%s'", acronym))
print("")

-- -----------------------------------------------
-- string.format — formatted output
-- -----------------------------------------------

print("=== string.format ===")

local profiles = {
    { name = "Tsunemori Akane",   rank = "Inspector",  hue_value = 18,  crime_coeff =  9  },
    { name = "Ginoza Nobuchika",  rank = "Inspector",  hue_value = 62,  crime_coeff = 44  },
    { name = "Kogami Shinya",     rank = "Enforcer",   hue_value = 99,  crime_coeff = 146 },
    { name = "Makishima Shogo",   rank = "Criminal",   hue_value = 999, crime_coeff = 299 },
    { name = "Masaoka Tomomi",    rank = "Enforcer",   hue_value = 88,  crime_coeff = 130 },
}

print(string.rep("-", 58))
for _, p in ipairs(profiles) do
    print(string.format("%-22s  %-12s  Hue:%3d  CrimeCoeff:%3d",
        p.name, p.rank, p.hue_value, p.crime_coeff))
end
print("")

-- -----------------------------------------------
-- string.find — pattern search (returns positions)
-- -----------------------------------------------

print("=== string.find ===")

local report = "Dominator activated. Kogami Shinya engaged target. Psycho-Pass clear."

-- Plain literal search (plain=true)
local s1, e1 = string.find(report, "Kogami", 1, true)
print(string.format("Position of 'Kogami': %d-%d", s1, e1))

-- Find first capitalized word using a pattern
local s2, e2, cap2 = string.find(report, "(%u%a+)", 1)
print(string.format("First capitalized word: '%s' (pos %d-%d)", cap2, s2, e2))

-- Returns nil when not found
local result = string.find(report, "Tsunemori")
print(string.format("Search for 'Tsunemori': %s", tostring(result)))
print("")

-- -----------------------------------------------
-- string.match — pattern match (returns content)
-- -----------------------------------------------

print("=== string.match ===")

local log1 = "CrimeCoeff:146 Enforcer:Kogami"
local log2 = "CrimeCoeff:299 Target:Makishima"
local log3 = "CrimeCoeff:9   Inspector:Tsunemori"

local function extract_coeff(log)
    return string.match(log, "CrimeCoeff:(%d+)")
end

print(string.format("log1 crime coeff: %s", extract_coeff(log1)))
print(string.format("log2 crime coeff: %s", extract_coeff(log2)))
print(string.format("log3 crime coeff: %s", extract_coeff(log3)))

local enforcer, coeff = string.match(log1, "CrimeCoeff:(%d+) Enforcer:(%a+)")
print(string.format("Enforcer=%s  CrimeCoeff=%s", enforcer, coeff))
print("")

-- -----------------------------------------------
-- string.gmatch — iterate over all matches
-- -----------------------------------------------

print("=== string.gmatch ===")

local patrol_route = "Sector-A,Sector-C,Sector-F,Sector-G,Sector-H"

print("Patrol sectors:")
for sector in string.gmatch(patrol_route, "[^,]+") do
    print(string.format("  %s", sector))
end

local name_list = "Tsunemori Akane, Ginoza Nobuchika, Kogami Shinya"
print("Name split (family given):")
for family, given in string.gmatch(name_list, "(%a+) (%a+)") do
    print(string.format("  Family=%-12s  Given=%s", family, given))
end
print("")

-- -----------------------------------------------
-- string.gsub — pattern substitution
-- -----------------------------------------------

print("=== string.gsub ===")

local classified = "Makishima Shogo was seen near the Sibyl System terminal."

local censored, count = string.gsub(classified, "Sibyl System", "[REDACTED]")
print(string.format("After substitution: %s", censored))
print(string.format("Substitution count: %d", count))

-- Pass a function to generate replacement strings dynamically
local raw_log = "Kogami:146, Masaoka:130, Makishima:299, Akane:9, Ginoza:44"
local masked = string.gsub(raw_log, "%d+", function(n)
    return string.rep("*", #n)
end)
print(string.format("Masked numbers: %s", masked))

-- Use a table to map names
local name_map = {
    Tsunemori = "常守",
    Kogami    = "狡噛",
    Ginoza    = "宜野座",
    Makishima = "槙島",
    Masaoka   = "征陸",
}
local jp_log = string.gsub(raw_log, "%a+", name_map)
print(string.format("Japanese names: %s", jp_log))
print("")

-- -----------------------------------------------
-- Pattern advanced: %b() matches balanced brackets
-- -----------------------------------------------

print("=== Pattern advanced: %b() ===")

local sys_log = "execute(scan_target) result(clear) action(stand_by)"

print("Arguments inside parentheses:")
for inner in string.gmatch(sys_log, "%b()") do
    local content = string.sub(inner, 2, -2)
    print(string.format("  %s", content))
end
print("")

-- -----------------------------------------------
-- Summary: extract and format information from a string
-- -----------------------------------------------

print("=== Summary: parsing a Dominator usage log ===")

local dominator_log = [[
2114-12-03 08:42 Kogami:146 target:Makishima mode:Lethal_Eliminator
2114-12-03 09:15 Akane:9 target:Victim01 mode:Non_Lethal_Paralyzer
2114-12-03 11:30 Ginoza:44 target:Suspect01 mode:Non_Lethal_Paralyzer
2114-12-03 14:20 Masaoka:130 target:Suspect02 mode:Lethal_Eliminator
]]

print(string.format("%-12s  %-8s  %-10s  %-10s  %s",
    "Datetime", "Officer", "CrimeCoeff", "Target", "Mode"))
print(string.rep("-", 70))

for line in string.gmatch(dominator_log, "[^\n]+") do
    local date, time_str, officer, coeff_str, target, mode =
        string.match(line,
            "(%d+%-%d+%-%d+) (%d+:%d+) (%a+):(%d+) target:(%S+) mode:(%S+)")
    if officer then
        print(string.format("%-12s  %-8s  %-10s  %-10s  %s",
            date .. " " .. time_str, officer, coeff_str, target, mode))
    end
end
lua psychopass_string.lua
=== string.len ===
Tsunemori Akane        len=15
Kogami Shinya          len=13
Ginoza Nobuchika       len=16
Makishima Shogo        len=15
Masaoka Tomomi         len=14
Method notation: Tsunemori Akane:len() = 15

=== string.sub ===
Family: Tsunemori  Given: Akane
Kogami Shinya last 5 bytes: hinya
Ginoza Nobuchika last 3 bytes: ika

=== string.upper / string.lower ===
Original : Psycho-Pass Level
upper    : PSYCHO-PASS LEVEL
lower    : psycho-pass level
Normalized (upper):
  safe                 -> SAFE
  LATENT_CRIMINAL      -> LATENT_CRIMINAL
  Clear                -> CLEAR
  ENFORCER             -> ENFORCER

=== string.rep ===
Gauge display : [********]
With separator: O-O-O-O-O
Crime coefficient danger bar:
  Tsunemori Akane        [#....] Lv1
  Ginoza Nobuchika       [##...] Lv2
  Kogami Shinya          [####.] Lv4
  Makishima Shogo        [#####] Lv5
  Masaoka Tomomi         [###..] Lv3

=== string.reverse ===
  DOM-001-AKANE            -> ENAKA-100-MOD
  DOM-002-SHINYA           -> AYNIHS-200-MOD
  DOM-003-NOBUCHIKA        -> AKIHCUBON-300-MOD

=== string.byte / string.char ===
byte('Akane')        = 65  (char 'A')
byte('Shinya', 1)    = 83  (char 'S')
byte('Kogami', 2,4)  = 111, 103, 97  (string 'oga')
string.char(72,83,66) = 'HSB'

=== string.format ===
----------------------------------------------------------
Tsunemori Akane        Inspector     Hue: 18  CrimeCoeff:  9
Ginoza Nobuchika       Inspector     Hue: 62  CrimeCoeff: 44
Kogami Shinya          Enforcer      Hue: 99  CrimeCoeff:146
Makishima Shogo        Criminal      Hue:999  CrimeCoeff:299
Masaoka Tomomi         Enforcer      Hue: 88  CrimeCoeff:130

=== string.find ===
Position of 'Kogami': 22-27
First capitalized word: 'Dominator' (pos 1-9)
Search for 'Tsunemori': nil

=== string.match ===
log1 crime coeff: 146
log2 crime coeff: 299
log3 crime coeff: 9
Enforcer=146  CrimeCoeff=Kogami

=== string.gmatch ===
Patrol sectors:
  Sector-A
  Sector-C
  Sector-F
  Sector-G
  Sector-H
Name split (family given):
  Family=Tsunemori    Given=Akane
  Family=Ginoza       Given=Nobuchika
  Family=Kogami       Given=Shinya

=== string.gsub ===
After substitution: Makishima Shogo was seen near the [REDACTED] terminal.
Substitution count: 1
Masked numbers: Kogami:***, Masaoka:***, Makishima:***, Akane:*, Ginoza:**
Japanese names: 狡噛:146, 征陸:130, 槙島:299, Akane:9, 宜野座:44

=== Pattern advanced: %b() ===
Arguments inside parentheses:
  scan_target
  clear
  stand_by

=== Summary: parsing a Dominator usage log ===
Datetime      Officer   CrimeCoeff  Target      Mode
----------------------------------------------------------------------
2114-12-03 08:42  Kogami    146         Makishima   Lethal_Eliminator
2114-12-03 09:15  Akane     9           Victim01    Non_Lethal_Paralyzer
2114-12-03 11:30  Ginoza    44          Suspect01   Non_Lethal_Paralyzer
2114-12-03 14:20  Masaoka   130         Suspect02   Lethal_Eliminator

Common Mistakes

Confusing function notation and method notation without understanding the difference

string.find(s, pat) (function notation) and s:find(pat) (method notation) are both valid. Method notation passes s as the implicit first argument (self) through the string metatable, so the two forms are internally equivalent. Mixing them carelessly makes it easy to pass s twice by accident.

-- NG: intending method notation but passing s a second time as an explicit argument
local s = "Kogami Shinya"
local result = s:find(s, "Kogami")  -- s is passed twice (once as self, once as the pattern)
print(result)  -- nil (searching for the pattern "Kogami Shinya" starting at "Kogami")
-- OK: pick one style and be explicit about the arguments
local s = "Kogami Shinya"
local result = string.find(s, "Kogami")  -- function notation: arguments are s, pat
print(result)  -- 1
-- or
local result2 = s:find("Kogami")  -- method notation: self is s, argument is pat only
print(result2)  -- 1

Using string.byte / string.char on multi-byte characters

string.byte() returns individual byte values. A Japanese character in UTF-8 occupies 3 bytes, so extracting only the first byte does not give a meaningful character value. string.char() likewise assembles a string from single byte values and cannot reconstruct multi-byte characters.

-- NG: using string.byte on a Japanese string produces unexpected values
local name = "常守朱"
local b = string.byte(name, 1)
print(b)               -- 229 (first byte of UTF-8 sequence for 常, not the codepoint)
print(string.char(b))  -- 'å' (a single byte, not the original character)
-- OK: use string.byte / string.char with ASCII strings
local code = "Tsunemori Akane"
local b = string.byte(code, 1)
print(b)               -- 84 (ASCII code for 'T')
print(string.char(b))  -- T

Writing a manual loop instead of using the third argument of string.rep

string.rep(s, n, sep) accepts a third argument sep that is inserted between each copy of s (Lua 5.2+). Not knowing about this argument leads to writing unnecessary loops.

-- NG: implementing a separator-joined repeat with a manual loop
local items = {}
for i = 1, 5 do
    items[i] = "Dominator"
end
local result = table.concat(items, " | ")
print(result)  -- works but verbose
-- OK: use the third argument of string.rep for the separator
local result = string.rep("Dominator", 5, " | ")
print(result)  -- Dominator | Dominator | Dominator | Dominator | Dominator

Overview

The string library is a standard library that is available without any require call. Every function can also be called as a method (s:upper() etc.). string.len() and string.sub() cover basic length and slicing; string.format() provides C printf-style formatting. The four pattern-matching functions (find, match, gmatch, gsub) use Lua's own pattern syntax — similar to regular expressions but without alternation or lookaheads. Pattern class specifiers such as %d, %a, and %s are combined with quantifiers *, +, ?, and -. Note that - is the shortest match quantifier while * is the longest. The third argument of string.gsub() can be a string, table, or function, enabling everything from simple replacement to dynamic transformation. For individual function details, see also string.find() / string.match(), string.gsub(), and string.format().

If you find any errors or copyright issues, please .