Split a string using string.gmatch() in Lua - string

There are some discussions here, and utility functions, for splitting strings, but I need an ad-hoc one-liner for a very simple task.
I have the following string:
local s = "one;two;;four"
And I want to split it on ";". I want, eventually, go get { "one", "two", "", "four" } in return.
So I tried to do:
local s = "one;two;;four"
local words = {}
for w in s:gmatch("([^;]*)") do table.insert(words, w) end
But the result (the words table) is { "one", "", "two", "", "", "four", "" }. That's certainly not what I want.
Now, as I remarked, there are some discussions here on splitting strings, but they have "lengthy" functions in them and I need something succinct. I need this code for a program where I show the merit of Lua, and if I add a lengthy function to do something so trivial it would go against me.

local s = "one;two;;four"
local words = {}
for w in (s .. ";"):gmatch("([^;]*);") do
table.insert(words, w)
end
By adding one extra ; at the end of the string, the string now becomes "one;two;;four;", everything you want to capture can use the pattern "([^;]*);" to match: anything not ; followed by a ;(greedy).
Test:
for n, w in ipairs(words) do
print(n .. ": " .. w)
end
Output:
1: one
2: two
3:
4: four

Just changing * to + works.
local s = "one;two;;four"
local words = {}
for w in s:gmatch("([^;]+)") do
table.insert(words, w)
print(w)
end
The magic character * represents 0 or more occurrene, so when it meet ',', lua regarded it as a empty string that [^;] does not exist.
Sorry for my carelessness, the words[3] should be a empty string, but when I run the original code in lua5.4 interpreter, everything works.
code here
running result here
(I have to put links because of lack of reputation)

function split(str,sep)
local array = {}
local reg = string.format("([^%s]+)",sep)
for mem in string.gmatch(str,reg) do
table.insert(array, mem)
end
return array
end
local s = "one;two;;four"
local array = split(s,";")
for n, w in ipairs(array) do
print(n .. ": " .. w)
end
result:
1:one
2:two
3:four

Related

Finding the "difference" between two string texts (Lua example)

I'm trying to find the difference in text between two string values in Lua, and I'm just not quite sure how to do this effectively. I'm not very experienced in working with string patterns, and I'm sure that's my downfall on this one. Here's an example:
-- Original text
local text1 = "hello there"
-- Changed text
local text2 = "hello.there"
-- Finding the alteration of original text with some "pattern"
print(text2:match("pattern"))
In the example above, I'd want to output the text ".", since that's the difference between the two texts. Same goes for cases where the difference could be sensitive to a string pattern, like this:
local text1 = "hello there"
local text2 = "hello()there"
print(text2:match("pattern"))
In this example, I'd want to print "(" since at that point the new string is no longer consistent with the old one.
If anyone has any insight on this, I'd really appreciate it. Sorry I couldn't give more to work with code-wise, I'm just not sure where to begin.
Just iterate over the strings and find when they don't match.
function StringDifference(str1,str2)
for i = 1,#str1 do --Loop over strings
if str1:sub(i,i) ~= str2:sub(i,i) then --If that character is not equal to it's counterpart
return i --Return that index
end
end
return #str1+1 --Return the index after where the shorter one ends as fallback.
end
print(StringDifference("hello there", "hello.there"))
local function get_inserted_text(old, new)
local prv = {}
for o = 0, #old do
prv[o] = ""
end
for n = 1, #new do
local nxt = {[0] = new:sub(1, n)}
local nn = new:sub(n, n)
for o = 1, #old do
local result
if nn == old:sub(o, o) then
result = prv[o-1]
else
result = prv[o]..nn
if #nxt[o-1] <= #result then
result = nxt[o-1]
end
end
nxt[o] = result
end
prv = nxt
end
return prv[#old]
end
Usage:
print(get_inserted_text("hello there", "hello.there")) --> .
print(get_inserted_text("hello there", "hello()there")) --> ()
print(get_inserted_text("hello there", "hello htere")) --> h
print(get_inserted_text("hello there", "heLlloU theAre")) --> LUA

Find first instance of a pattern in Lua and remove it from string

I get strings in the following format:
abc:321,cba:doodoo,hello:world,eat:mysh0rts
I'd like to grab one instance of the data pairing from the string and remove it from the string, so for example if I wanted to grab the value following hello:world I'd like this to happen:
local helloValue, remainingString = GetValue("hello")
Which will return world for hellovalue and abc:321,cba:doodoo,eat:mysh0rts for remainingString.
I'm doing this rather cumbersomely with loops, what would a better way of doing it be?
This is one way:
local str = 'abc:321,cba:doodoo,hello:world,eat:mysh0rts'
local t = {}
for k, v in str:gmatch('(%w+):(%w+)') do
if k ~= 'hello' then
table.insert(t, k .. ':' .. v)
else
helloValue = v
end
end
remainingString = table.concat(t, ',')
print(helloValue, remainingString)
You can turn this to a more general GetValue function yourself.
Try also this:
local str = 'abc:321,cba:doodoo,hello:world,eat:mysh0rts'
function GetValue(s,k)
local p=k..":([^,]+),?"
local a=s:match(p)
local b=s:gsub(p,"")
return a,b
end
print(GetValue(str,"hello"))
print(GetValue(str,"eat"))
If you want to parse the whole string into key-value pairs, try this:
for k,v in str:gmatch("(.-):([^,]+),?") do
print(k,v)
end
(hello:[^,]+,)
Just do a replace with empty string.The replace data and $1 are the things you want.See demo.
http://regex101.com/r/yR3mM3/24

How to concatenate strings into one using loop?

can someone help me with string concatenate problem. I read data from register. It's function utf(regAddr, length). I get table with decimal numbers, then I transform it into hex and to string in loop. I need concatenate these strings into one.
there is not in Lua something like .= operator
function utf(regAddr, length)
stringTable = {}
table.insert(stringTable, {mb:readregisters(regAddr-1,length)})
for key, value in pairs(stringTable) do
for i=1, length do
v = value[i]
v = lmcore.inttohex(v, 4)
v = cnv.hextostr(v)
log(v)
end
end
end
-- function(regAddr, length)
utf(30,20)
There is no append operator for strings. Strings are immutable values.
The .. operator concatenates two string, producing a third string as a result:
local b = "con"
local c = "catenate"
local a = b .. c -- "concatenate"
The table.concat function concatenates strings in a table, producing a string result:
local t = { "con", "catenate" }
local a = table.concat(t) -- "concatenate"
local t = { "two", "words" }
local a = table.concat(t, " ") -- "two words"
The string.format function takes a format pattern with a list of compatible values, producing a string result:
local b = 2
local c = "words"
local a = string.format("%i %s", b, c) -- "2 words"
local t = { 2, "words" }
local a = string.format("%i %s", unpack(t)) -- "2 words"
If you are accumulating a lot of strings that you eventually want to concatenate, you can use a table as a temporary data structure and concatenate when you are done accumulating:
local t = {}
for i = 1, 1000 do
table.insert(t, tostring(i))
end
local a = table.concat(t) -- "1234...9991000"
For a very large number of strings, you can concatenate incrementally. See LTN 9: Creating Strings Piece by Piece and related discussions.
You should try the table.concat method.
Maybe this other question can help you:
Lua table.concat
Checkout this tutorial http://lua-users.org/wiki/TableLibraryTutorial
this code works:
function utf(regAddr, length)
stringTable = {}
table.insert(stringTable, {mb:readregisters(regAddr-1,length)})
for key, value in pairs(stringTable) do
t = {}
for i=1, length do
v = value[i]
v = lmcore.inttohex(v, 4)
v = cnv.hextostr(v)
table.insert(t, v)
end
a = table.concat(t)
end
end
-- function(regAddr, length)
utf(30,20)

String Manipulation in Lua: Make the odd char uppercase

I'm trying to do a library in Lua with some function that manipulate strings.
I want to do a function that changes the letter case to upper only on odd characters of the word.
This is an example:
Input: This LIBRARY should work with any string!
Result: ThIs LiBrArY ShOuLd WoRk WiTh AnY StRiNg!
I tried with the "gsub" function but i found it really difficult to use.
This almost works:
original = "This LIBRARY should work with any string!"
print(original:gsub("(.)(.)",function (x,y) return x:upper()..y end))
It fails when the string has odd length and the last char is a letter, as in
original = "This LIBRARY should work with any strings"
I'll leave that case as an exercise.
First, split the string into an array of words:
local original = "This LIBRARY should work with any string!"
local words = {}
for v in original:gmatch("%w+") do
words[#words + 1] = v
end
Then, make a function to turn words like expected, odd characters to upper, even characters to lower:
function changeCase(str)
local u = ""
for i = 1, #str do
if i % 2 == 1 then
u = u .. string.upper(str:sub(i, i))
else
u = u .. string.lower(str:sub(i, i))
end
end
return u
end
Using the function to modify every words:
for i,v in ipairs(words) do
words[i] = changeCase(v)
end
Finally, using table.concat to concatenate to one string:
local result = table.concat(words, " ")
print(result)
-- Output: ThIs LiBrArY ShOuLd WoRk WiTh AnY StRiNg
Since I am coding mostly in Haskell lately, functional-ish solution comes to mind:
local function head(str) return str[1] end
local function tail(str) return substr(str, 2) end
local function helper(str, c)
if #str == 0 then
return ""
end
if c % 2 == 1 then
return toupper(head(str)) .. helper(tail(str),c+1)
else
return head(str) .. helper(tail(str), c+1)
end
end
function foo(str)
return helper(str, 1)
end
Disclaimer: Not tested, just showing the idea.
And now for real, you can treat a string like a list of characters with random-access with reference semantics on []. Simple for loop with index should do the trick just fine.

Lua - Removing words that are not in a list

i want to remove words that are not in a list, from a string.
for example i have the string "i like pie and cake" or "pie and cake is good" and i want to remove words that are not "pie" or "cake" and end out with a string saying "pie cake".
it would be great, if the words it does not delete could be loaded from a table.
Here's another solution, but you may need to trim the last space in the result.
acceptable = { "pie", "cake" }
for k,v in ipairs(acceptable) do acceptable[v]=v.." " end
setmetatable(acceptable,{__index= function () return "" end})
function strip(s,t)
s=s.." "
print('"'..s:gsub("(%a+) %s*",t)..'"')
end
strip("i like pie and cake",acceptable)
strip("pie and cake is good",acceptable)
gsub is the key point here. There are other variations using gsub and a function, instead of setting a metatable for acceptable.
local function stripwords(inputstring, inputtable)
local retstring = {}
local itemno = 1;
for w in string.gmatch(inputstring, "%a+") do
if inputtable[w] then
retstring[itemno] = w
itemno = itemno + 1
end
end
return table.concat(retstring, " ")
end
Provided that the words you want to keep are all keys of the inputtable.
The following also implements the last part of the request (I hope):
it would be great, if the words it does not delete could be loaded from a table.
function stripwords(str, words)
local w = {};
return str:gsub("([^%s.,!?]+)%s*", function(word)
if words[word] then return "" end
w[#w+1] = word
end), w;
end
Keep in mind that the pattern matcher of Lua is not compatible with multibyte strings. This is why I used the pattern above. If you don't care about multibyte strings, you can use something like "(%a+)%s". In that case I would also run the words through string.upper
Tests / Usage
local blacklist = { some = true, are = true, less = true, politics = true }
print((stripwords("There are some nasty words in here!", blacklist)))
local r, t = stripwords("some more are in politics here!", blacklist);
print(r);
for k,v in pairs(t) do
print(k, v);
end

Resources