luaguides

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.

#NameTypeDefaultDescription
1sstring (or number)requiredThe 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

  1. Bytes, not characters. This is the single biggest footgun. string.len("é") returns 2, not 1. Use utf8.len for character counts.
  2. Empty string is length 0, not an error. #"" and (""):len() both return 0.
  3. NUL bytes are counted. Unlike C’s strlen, Lua strings have no terminator. Useful for binary protocols, dangerous if you assumed C-style behavior.
  4. 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").
  5. 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".
  6. # vs string.len is a wash. They compile to the same VM op. The operator is one character shorter; the function reads more like a call.
  7. Do not use on tables. string.len({1, 2, 3}) errors. For tables, use #t or rawlen.
  8. Not a substitute for UTF-8 validation. string.len happily returns a count for invalid UTF-8. To validate, call utf8.len (which yields nil on bad input) or pcall(utf8.len, s).
  9. Locale-independent. string.len ignores the C locale. string.lower and string.upper are the functions that consult it.
  10. s:len() needs parens on a literal. "hi":len() is a syntax error; use ("hi"):len().

See Also