util.string.grapheme_indices

Shared

Returns an iterator over the byte ranges of each grapheme cluster


Syntax

local iterator = util.string.grapheme_indices(
    input,
    start_at = 1,
    end_at = -1
)

This function's iterator returns byte positions not character positions.

Passing those positions into util.string.sub or util.string.byte - which now index by character will give incorrect results except by coincidence on pure-ASCII input.

Parameters

TypeNameDescription
stringinputString to iterate over
intstart_atByte position to begin from - negative counts from end
intend_atByte position to end at (inclusive) - negative counts from end

Returns

TypeNameDescription
functioniteratorIterator function for use in a for loop, yielding start_byte, end_byte per grapheme cluster

Examples

Iterate over grapheme clusters in plain text
for first, last in util.string.grapheme_indices("abc") do
    core.engine.print("info", first, last)
end

--[[
Output:
1  1
2  2
3  3
]]
A multi-codepoint emoji counts as a single grapheme cluster
-- Family emoji built from 4 codepoints joined by zero-width joiners
local text = "๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ"
local clusters = 0

for _ in util.string.grapheme_indices(text) do
    clusters = clusters + 1
end

core.engine.print("info", clusters) -- 1
Count grapheme clusters versus raw codepoints
-- A family emoji is one visible cluster built from 7 codepoints
-- (4 person emoji joined by 3 zero-width joiners)
local text = "๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ"
local clusters, codepoints = 0, 0

for _ in util.string.grapheme_indices(text) do
    clusters = clusters + 1
end
for _ in util.string.codes(text) do
    codepoints = codepoints + 1
end

core.engine.print("info", clusters, codepoints) -- 1  7

On this page