string.len
string.len(s) Overview
string.len returns the length of a string measured in bytes. For any string s in Lua 5.4, string.len(s), s:len(), and the length operator #s all yield the same value: the number of bytes in the string, including any embedded NUL bytes.
One thing to remember up front: string.len counts bytes, not characters. A non-ASCII letter such as é is two bytes in UTF-8, so string.len("é") produces 2. For codepoint counts, use utf8.len from the standard utf8 library.
Signature
string.len takes a single argument and produces an integer.
string.len(s) -- canonical form
s:len() -- method form, sugar via the string metatable
#s -- length operator, defined to be equivalent for strings
The function form and the method form compile to the same VM instruction in Lua 5.4 (OP_LEN). The length operator on strings is also a direct call to the same machinery, so there is no performance reason to prefer one over the other. Pick whichever reads better at the call site.
Parameters
string.len takes one parameter, s, with the following behavior.
| # | Name | Type | Default | Description |
|---|---|---|---|---|
| 1 | s | string (or number) | required | The string whose length is wanted. Numbers are coerced to strings first, so string.len(42) returns 2. Booleans, nil, tables, functions, threads, and userdata raise an error: bad argument #1 to 'len' (string expected, got X). |
There are no optional parameters and no overloads.
Return Value
string.len produces a single integer (the Lua 5.3+ integer subtype, 64-bit on standard 64-bit builds). The result is the number of bytes in s, including any embedded NUL bytes. The empty string "" returns 0. The manual guarantees the result fits in a Lua integer, which is not a practical limit on 64-bit builds.
Examples
The examples below assume a default Lua 5.4 build. Run them in lua -i to see the printed output.
Basic lengths and the empty string
print(string.len("Lua")) --> 3
print(string.len("")) --> 0
print(string.len("hello world")) --> 11
The empty string returns 0, not an error. That is the usual way to ask “is this string empty?” in Lua, though s == "" usually reads better.
Embedded NUL bytes are counted
Lua strings are 8-bit clean: there is no terminator byte, and a 0 inside a string is just another byte. This is the manual’s own example.
local s = "a\000bc\000"
print(#s) --> 5
print(s:len()) --> 5
print(string.len(s)) --> 5
C programmers used to strlen find this surprising. In Lua, "\0" is a valid one-character string of length 1, and any sequence of bytes is a valid string.
Bytes, not Unicode characters
print(string.len("é")) --> 2 -- 2 bytes: 0xC3 0xA9
print(string.len("hello é")) --> 8 -- 7 characters, 8 bytes (é is 2)
-- For character counts, use utf8.len:
print(utf8.len("é")) --> 1
print(utf8.len("hello é")) --> 7
For any input that might contain non-ASCII text, prefer utf8.len over string.len. utf8.len also yields nil plus an error position when the string is not valid UTF-8, which makes it a useful validation step.
Number coercion
The string library coerces numbers to strings before measuring them.
print(string.len(42)) --> 2
print(string.len(3.14)) --> 4
print(string.len(-100)) --> 4
print(string.len(0)) --> 1
This is sometimes a convenience, sometimes a footgun. If you expect a string and a table arrives, you get an error rather than a silent coercion.
Method and operator forms
local s = "Lua 5.4"
print(s:len()) --> 6
print((""):len()) --> 0 -- parens required to bind the literal
print(("multi\nline"):len()) --> 10 -- the newline is one byte (0x0A)
A bare method call on a string literal needs parentheses: "hi":len() is a syntax error. The parser cannot see a method call starting on a string literal without help.
Iterating up to length
#s is the natural upper bound for a for loop that walks a string byte by byte.
local s = "hello"
for i = 1, #s do
print(i, s:byte(i))
end
--> 1 104
--> 2 101
--> 3 108
--> 4 108
--> 5 111
Pair this with string.byte when you want to inspect or transform individual bytes.
Building a safe length helper
When string.len is called on data you did not construct yourself, validate the type first. The wrapped helper below turns a silent type error into a message you can log.
local function safe_len(s)
assert(type(s) == "string", "expected string, got " .. type(s))
return s:len()
end
print(safe_len("hi")) --> 2
print(safe_len(42)) -- assertion fails: expected string, got number
print(safe_len({1, 2, 3})) -- assertion fails: expected string, got table
Use this pattern on user input, decoded JSON values, and network payloads. It keeps the call site readable while still surfacing unexpected types early.
Common Pitfalls
- Bytes, not characters. This is the single biggest footgun.
string.len("é")returns2, not1. Useutf8.lenfor character counts. - Empty string is length 0, not an error.
#""and(""):len()both return0. - NUL bytes are counted. Unlike C’s
strlen, Lua strings have no terminator. Useful for binary protocols, dangerous if you assumed C-style behavior. - Auto-coerces numbers, errors on other types. If you expect a string and got a table, the error message is your only signal. Validate inputs at boundaries with
assert(type(s) == "string", "expected string"). - Produces an integer, not a float. Since Lua 5.3, the result is the integer subtype. If you do strict type checks,
math.type(string.len(s))is"integer". #vsstring.lenis a wash. They compile to the same VM op. The operator is one character shorter; the function reads more like a call.- Do not use on tables.
string.len({1, 2, 3})errors. For tables, use#torrawlen. - Not a substitute for UTF-8 validation.
string.lenhappily returns a count for invalid UTF-8. To validate, callutf8.len(which yieldsnilon bad input) orpcall(utf8.len, s). - Locale-independent.
string.lenignores the C locale.string.lowerandstring.upperare the functions that consult it. s:len()needs parens on a literal."hi":len()is a syntax error; use("hi"):len().
See Also
string.byte— read individual bytes by index, naturally bounded by#sstring.char— the inverse: bytes to a one-character stringstring.find— returns byte offsets in the same coordinate system asstring.lenstring.format— pad strings to a fixed lengthstring.gmatch— pattern iteration; often checks#sfirstrawlen— length that bypasses__len; for plain strings returns the same valuetostring—string.len(tostring(x))is a common idiom for sizing a coerced string- Strings and Patterns tutorial — beginner walkthrough of the string library
- Pattern Matching tutorial — patterns use
#heavily - Lua String Patterns guide — pattern cookbook
- Lua Binary Data guide — where the “8-bit clean / NUL counted” property matters most