Lua Split String Function

For anyone interested, I created a function that allows strings to be split with a delimiter of any character and any number of characters. I am very open if anyone would like to improve my code in any way. :slight_smile:

local explode = function(str, delimiter)
    local result = {};
    local startPoint, endPoint = 1, 1;
    for i = 1, str:len() do
        if (i + 1) > str:len() then
            endPoint = str:len();
        end
        if (str:sub(i, i + (delimiter:len() - 1)) == delimiter) then
            endPoint = i - 1;
            table.insert(result, str:sub(startPoint, endPoint));
            startPoint = i + (delimiter:len());
        elseif (i == str:len()) then
            endPoint = str:len();
            table.insert(result, str:sub(startPoint, endPoint));
        end
    end
    return result;
end

Example

local foods = "apple$%$%orange$%$%tomato$%$%pizza";
local foodTable = explode(foods, "$%$%");

for i, food in pairs(foodTable)  do
    print(food);
end

Output

> apple
> orange
> tomato
> pizza
3 Likes

wow nice

local str = "apple$%$%orange$%$%tomato$%$%pizza"

for i in string.gmatch(str, "[^$%%$%%]+") do
    print(i)
end

apple
orange
tomato
pizza

6 Likes

Oh damn

You beat me to it.

local str = "apple$orange%tomato$pizza"

for i in string.gmatch(str, "[^$%%$%%]+") do
    print(i)
end

apple
orange
tomato
pizza

Not what OP wants.

How come?

As in, delimited by a sequence of characters, not just any characters from a set (at least that’s what the code given in OP does).

So the whole %% sequence must appear for the string to be split there.

Revised.

local function explode(str, delimiter)
    local fragments = {}
    local previousPosition = 1
    for pos in string.gmatch(str, "()(" .. string.gsub(delimiter, "%%", "%%%%") ..")") do
        table.insert(fragments, string.sub(str, previousPosition, pos - 1))		
        previousPosition = pos + string.len(delimiter)
    end
    
    table.insert(fragments, string.sub(str, previousPosition, string.len(str)))
    
    return fragments
end

print(table.concat(explode("apple$%$%orange$%$%tomato$%$%pizza", "$%$%"), "\n"))

Why does nobody use string.find? It has this nice parameter to ignore string patterns:

local function explode(str,delimiter)
	local res,i = {},1
	while i <= #str do
		local a,b = str:find(delimiter,i,true)
		if not a then break end
		table.insert(res,str:sub(i,a-1))
		i = b + 1
	end
	table.insert(res,str:sub(i))
	return res
end
for k,v in pairs(explode("Dis:.:is:.:weird",":.:")) do
	print(k..".",v)
end
--> 1. Dis
--> 2. is
--> 3. weird

Exploding ":.:Dis:.:is:.:weird:.:" would result in {"","Dis","is","weird",""}, which is fine?

Link to info about all parameters: http://lua-users.org/wiki/StringLibraryTutorial
(well, it doesn’t fully explain the format magic of string.format, but eh)

1 Like
local tab = {"hello m8!!","there are 2 many","Worlds"}

local divider="BBB"
local divlen = string.len(divider)

function TableToString(t)
 local str = ""
 for i = 1,#t do
  str = str .. t[i] .. divider
 end
 return str
end

function StringToTable(s)
 local t = {}
 for k in string.gmatch(s, ".-"..divider) do
     t[#t+1]=string.sub(k,0,string.len(k)-divlen)
 end
 return t
end

local str = TableToString(tab)

print(str)
-->hello m8!!BBBthere are 2 manyBBBWorldsBBB

local t = StringToTable(str)

for k,v in pairs(t) do
	print(k..".",v)
end

-->1. hello m8!!
-->2. there are 2 many
-->3. Worlds

This might be the fastest solution? Not sure about that but with this way, it’s pretty easy.

Or if you’re lazy and want an actual function parameter for using regex and capping results:

local function explode(str, delim, useRegex, maxResults) -- useRegex defaults to false, maxResults defaults to infinite
	local results = {}
	maxResults = maxResults or -1
	if #str > 0 and maxResults ~= 0 then
		local num, start = 1, 1
		local i1, i2 = str:find(delim, start, not useRegex)
		while i1 and maxResults ~= 1 do
			results[num] = str:sub(start, i1-1)
			num = num+1
			start = i2+1
			i1, i2 = str:find(delim, start, not useRegex)
			maxResults = maxResults-1
		end
		results[num] = str:sub(start)
	end
	return results
end

for i, v in pairs(explode("-Dis:.:works:.:fine//", ":.:")) do
	print(i .. ".", v)
end

--> 1. -Dis
--> 2. works
--> 3. fine//

for i, v in pairs(explode("-Dis:.:works:.:fine//", ":.:", false, 2)) do
	print(i .. ".", v)
end

--> 1. -Dis
--> 2. works:.:fine//

for i, v in pairs(explode("-Dis:.:works:.:fine//", "%w+", true)) do
	print(i .. ".", v)
end

--> 1. -
--> 2. :.:
--> 3. :.:
--> 4. //

I had this hidden away in some script I wrote a year ago. Might be helpful? Not sure if I wrote this or got it online somewhere.

local function SplitString(str, sep)
	local fields = {}
	local pattern = string.format("([^%s]+)", sep)
	str:gsub(pattern, function(c) fields[#fields + 1] = c end)
	return fields
end

sep is the delimiter. Example use:

local str = "Test,ABC,123,XYZ"
local items = SplitString(str, ",")
for _,item in pairs(items) do print(item) end

Source

2 Likes

Nice work! I thought about using string:find

lots of split functions going on over here http://lua-users.org/wiki/SplitJoin

1 Like

Ah, there’s where my version came from!