So, I have a project that requires lots of string manipulation. I want to match three strings in a main string by matching all the text inside a square bracket. Except this outputs 'c' as nil? Any help?
a,b,c = x:match("%[(%a+)%],%[(%a+)%]")
print(c)
print(b)
if vars[b] == vars[tc] then
print(vars[b])
end
You forgot one pattern:
x=[[
*DISPLAY[AL][DL], [XX]
]]
a,b,c = x:match("%[(%a+)%]%[(%a+)%], %[(%a+)%]")
print(a,b,c)
Related
I'm (again) stuck because patterns... so let's see if with a little of help... The case is I have e. g. a string returned by a function that contains the following:
📄 My Script
ScriptID:RL_SimpleTest
Version:0.0.1
ScriptType:MenuScript
AnotherKey:AnotherValue
And, maybe, some more text...
And I'd want to parse it line by line and should the line contains a ":" get the left side content of the line in a variable (k) and the right content in another one (v), so e. g. I'd have k containing "ScriptID" and v containing "RL_SimpleTest" for the second line (the first one should be just ignored) and so on...
Well, I've started with something like this:
function RL_Test:StringToKeyValue(str, sep1, sep2)
sep1 = sep1 or "\n"
sep2 = sep2 or ":"
local t = {}
for line in string.gmatch(str, "([^" .. sep1 .. "]+)") do
print(line)
for k in string.gmatch(line, "([^" .. sep2 .. "]+)") do --Here is where I'm lost trying to get the key/value pair separately and at the same time...
--t[k] = v
print(k)
end
end
return t
end
With the hope once I got isolated the line containing the data in the key:value form that I want to extract, I'd be able to do some kind of for k, v in string.gmatch(line, "([^" .. sep2 .. "]+)") or something so and that way get the two pieces of data, but of course it doesn't work and even though I have a feeling it's a triviality I don't know even where to start, always for the lack of patterns understanding...
Well, I hope at least I exposed it right... Thanks in advance for any help.
local t = {}
for line in (s..'\n'):gmatch("(.-)\r?\n") do
for a, b in line:gmatch("([^:]+):([^:\n\r]+)") do
t[a] = b
end
end
The pattern is quite simple. Match anything that is not a colon that is followed by a colon that is followed by anything that is not a colon or a line break. Put what you want in captures and you're done.
I assume every line is of the format k:v, containing exactly one colon, or containing no colon (no k/v pair).
Then you can simply first match nonempty lines using [^\n]+ (assuming UNIX LF line endings), then match each line using ^([^:]+):([^:]+)$. Breakdown of the second pattern:
^ and $ are anchors. They force the pattern to match the entire line.
([^:]+) matches & captures one or more non-semicolon characters.
This leaves you with:
function RL_Test:StringToKeyValue(str)
local t = {}
for line in str:gmatch"[^\n]+" do
local k, v = line:match"^([^:]+):([^:]+)$"
if k then -- line is k:v pair?
t[k] = v
end
end
return t
end
If you want to support Windows CRLF line endings, use for line in (s..'\n'):gmatch'(.-)\r?\n' do as in Piglet's answer for matching the lines instead.
This answer differs from Piglet's answer in that it uses match instead of gmatch for matching the k/v pairs, allowing exactly one k/v pair with exactly one colon per line, whereas Piglet's code may extract multiple k/v pairs per line.
Some context, This is some of my first programming in Lua and I am doing it with QSC software.
local a = Controls.Flippy.String
local b = Controls.Flippy.Value
local c = Controls.Flippy.Position
local d = " "
print(a..d..b..d..c)
This worked but is there away I can write strings in line with a variables. Such as:
print("controls "a" "b" "c)
Same way you put the strings together--with .., the concatenation operator.
print("controls "..a.." "..b.." "..c)
It is also possible to use print with multiple arguments, though the spacing is often different (which may or may not be desirable):
print("controls",a,b,c)
You can just write the strings as
print("controls", a, b, c)
You can choose not to put spaces in between if you wish not to.
Hope that helps!
There are several ways to print multiple values in a line.
local name = "Frank"
local age = 15
-- simple, but not applicable for my example due to formatting:
print("My name is", name, "I'm", age, "years old")
-- using the concatenation operator ..
print("My name is " .. name .. ". I'm " .. age .. " years old")
-- using string.format, best for complex expressions, capable of advanced formatting
print(string.format("My name is %s. I'm %d years old.", name, age))
I'm trying to learn how patterns (implemented in string.gmatch, etc.) do work in Lua 5.3, from the reference manual.
(Thanks #greatwolf for correcting my interpretation about the pattern item using *.)
What I'm trying to do is to match '(%(.*%))*' (substrings enclosed by ( and ); for example, '(grouped (etc))'), so that it logs
(grouped (etc))
(etc)
or
grouped (etc)
etc
But it does nothing 😐 (online compiler).
local test = '(grouped (etc))'
for sub in test:gmatch '(%(.*%))*' do
print(sub)
end
Another possibility -- using recursion:
function show(s)
for s in s:gmatch '%b()' do
print(s)
show(s:sub(2,-2))
end
end
show '(grouped (etc))'
I don't think you can do this with gmatch but using %b() along with the while loop may work:
local pos, _, sub = 0
while true do
pos, _, sub = ('(grouped (etc))'):find('(%b())', pos+1)
if not sub then break end
print(sub)
end
This prints your expected results for me.
local test = '(grouped (etc))'
print( test:match '.+%((.-)%)' )
Here:
. +%( catch the maximum number of characters until it %( ie until the last bracket including it, where %( just escapes the bracket.
(.-)%) will return your substring to the first escaped bracket %)
I'm attempting to simplify a script, and my attempts are failing. I'm making a function that will pass the given arguments and turn them into an indexed table, but I want to be able to pass quoted and non-quoted alike and have the function recognize that quoted arguments are considered one value while also respecting non-quoted arguments.
For example:
makelist dog "brown mouse" cat tiger "colorful parrot"
should return an indexed table like the following:
list_table = {"dog", "brown mouse", "cat", "tiger", "colorful parrot"}
The code I have works for quoted, but it's messing up on the non-quoted, and on top of that, adds the quoted arguments a second time. Here's what I have:
function makelist(str)
require 'tprint'
local list_table = {}
for word in string.gmatch(str, '%b""') do
table.insert(list_table, word)
end
for word in string.gmatch(str, '[^%p](%a+)[^%p]') do
table.insert(list_table, word)
end
tprint(list_table)
end
I'm not understanding why the omission of quotes is being ignored, and also is chopping off the first letter. That is, this is the output I receive from tprint (a function that prints a table out, not relevant to the code):
makelist('dog "brown mouse" cat tiger "colorful parrot"')
1=""brown mouse""
2=""colorful parrot""
3="og"
4="rown"
5="mouse"
6="cat"
7="tiger"
8="olorful"
9="parrot"
As you can see, 'd', 'b', and 'c' are missing. What fixes do I need to make so that I can get the following output instead?
1="brown mouse"
2="colorful parrot"
3="dog"
4="cat"
5="tiger"
Or better yet, have them retain the same order they were dictated as arguments, if that's possible at all.
local function makelist(str)
local t = {}
for quoted, non_quoted in ('""'..str):gmatch'(%b"")([^"]*)' do
table.insert(t, quoted ~= '""' and quoted:sub(2,-2) or nil)
for word in non_quoted:gmatch'%S+' do
table.insert(t, word)
end
end
return t
end
It may be easier to simply split on whitespaces and concatenate those elements that are inside quotes. Something like this may work (I added few more test cases):
function makelist(str)
local params, quoted = {}, false
for sep, word in str:gmatch("(%s*)(%S+)") do
local word, oquote = word:gsub('^"', "") -- check opening quote
local word, cquote = word:gsub('"$', "") -- check closing quote
-- flip open/close quotes when inside quoted string
if quoted then -- if already quoted, then concatenate
params[#params] = params[#params]..sep..word
else -- otherwise, add a new element to the list
params[#params+1] = word
end
if quoted and word == "" then oquote, cquote = 0, oquote end
quoted = (quoted or (oquote > 0)) and not (cquote > 0)
end
return params
end
local list = makelist([[
dog "brown mouse" cat tiger " colorful parrot " "quoted"
in"quoted "terminated by space " " space started" next "unbalanced
]])
for k, v in ipairs(list) do print(k, v) end
This prints the following list for me:
1 dog
2 brown mouse
3 cat
4 tiger
5 colorful parrot
6 quoted
7 in"quoted
8 terminated by space
9 space started
10 next
11 unbalanced
First thanks for your question, got me to learn the basics of Lua!
Second, so I think you went with your solution in a bit of misdirection. Looking at the question I just said why don't you split once by the quotes (") and than choose where you want to split by space.
This is what I came up with:
function makelist(str)
local list_table = {}
i=0
in_quotes = 1
if str:sub(0,1) == '"' then
in_quotes = 0
end
for section in string.gmatch(str, '[^"]+') do
i = i + 1
if (i % 2) == in_quotes then
for word in string.gmatch(section, '[^ ]+') do
table.insert(list_table, word)
end
else
table.insert(list_table, section)
end
end
for key,value in pairs(list_table) do print(key,value) end
end
The result:
1 dog
2 brown mouse
3 cat
4 tiger
5 colorful parrot
I am using LUA to create a table within a table, and am running into an issue. I need to also populate the NIL values that appear, but can not seem to get it right.
String being manipulated:
PatID = '07-26-27~L73F11341687Per^^^SCI^SP~N7N558300000Acc^'
for word in PatID:gmatch("[^\~w]+") do table.insert(PatIDTable,word) end
local _, PatIDCount = string.gsub(PatID,"~","")
PatIDTableB = {}
for i=1, PatIDCount+1 do
PatIDTableB[i] = {}
end
for j=1, #PatIDTable do
for word in PatIDTable[j]:gmatch("[^\^]+") do
table.insert(PatIDTableB[j], word)
end
end
This currently produces this output:
table
[1]=table
[1]='07-26-27'
[2]=table
[1]='L73F11341687Per'
[2]='SCI'
[3]='SP'
[3]=table
[1]='N7N558300000Acc'
But I need it to produce:
table
[1]=table
[1]='07-26-27'
[2]=table
[1]='L73F11341687Per'
[2]=''
[3]=''
[4]='SCI'
[5]='SP'
[3]=table
[1]='N7N558300000Acc'
[2]=''
EDIT:
I think I may have done a bad job explaining what it is I am looking for. It is not necessarily that I want the karats to be considered "NIL" or "empty", but rather, that they signify that a new string is to be started.
They are, I guess for lack of a better explanation, position identifiers.
So, for example:
L73F11341687Per^^^SCI^SP
actually translates to:
1. L73F11341687Per
2.
3.
4. SCI
5. SP
If I were to have
L73F11341687Per^12ABC^^SCI^SP
Then the positions are:
1. L73F11341687Per
2. 12ABC
3.
4. SCI
5. SP
And in turn, the table would be:
table
[1]=table
[1]='07-26-27'
[2]=table
[1]='L73F11341687Per'
[2]='12ABC'
[3]=''
[4]='SCI'
[5]='SP'
[3]=table
[1]='N7N558300000Acc'
[2]=''
Hopefully this sheds a little more light on what I'm trying to do.
Now that we've cleared up what the question is about, here's the issue.
Your gmatch pattern will return all of the matching substrings in the given string. However, your gmatch pattern uses "+". That means "one or more", which therefore cannot match an empty string. If it encounters a ^ character, it just skips it.
But, if you just tried :gmatch("[^\^]*"), which allows empty matches, the problem is that it would effectively turn every ^ character into an empty match. Which is not what you want.
What you want is to eat the ^ at the end of a substring. But, if you try :gmatch("([^\^])\^"), you'll find that it won't return the last string. That's because the last string doesn't end with ^, so it isn't a valid match.
The closest you can get with gmatch is this pattern: "([^\^]*)\^?". This has the downside of putting an empty string at the end. However, you can just remove that easily enough, since one will always be placed there.
local s0 = '07-26-27~L73F11341687Per^^^SCI^SP~N7N558300000Acc^'
local tt = {}
for s1 in (s0..'~'):gmatch'(.-)~' do
local t = {}
for s2 in (s1..'^'):gmatch'(.-)^' do
table.insert(t, s2)
end
table.insert(tt, t)
end