Correct pattern doesn't work in Lua gmatch - lua

For example we have a string
local str = "12345:some.address.ru:1234"
And we need parse this string as:
var1 = "12345" -- mandatory
var2 = "some.address.ru" -- can be nil
var3 = "1234" -- can be nil
I've written such code:
for var1, var2, var3 in str:gmatch('^(%d+)%:?([%a.]*)%:(%d+)$') do
print(var1)
print(var2)
print(var3)
end
but I doesn't receive any result. And if I delete simbol ^ in the beginning of the pattern it works well.
What 's the problem? Why doesn't it work with simbol ^ and how can I fix it?
(I need to check that this pattern starts from beginning of the string)
And is there any chance to do this work without for loop?
(My string doesn't contain more then 1 pattern)
Thanks

The manual says this about gmatch:
a caret '^' at the start of a pattern does not work as an anchor, as this would prevent the iteration.
You don’t need a loop and so don't need gmatch. Just do
var1, var2, var3=str:match('(%d+)%:?([%a.]*)%:(%d+)$')
print(var1)
print(var2)
print(var3)
Adding ^ to the pattern is harmless.
A simpler pattern is '(.-):(.-):(.-)$’.
Note that in both cases you don’t need to anchor the pattern at the beginning but you do need to anchor it at the end.

Related

string.match is too broad, is there any other alternatives that would work better?

Currently I am getting passed a bunch of strings from a home automation controller to our driver which I am currently developing.
I receive messages such as ZAA and other code which may be AA but the string.match sometimes will match the AA with the ZAA if statement.
This issue is much more wide spread than just those two strings (probably around to 10-15 other similarities).
I understand that I could add more conditions to the if/elseif statements but surely there is an exact match version?
Any ideas would be greatly appreciated.
Example; Even though the string is "AA" it will match "ZAA"
stringInput = "AA"
if string.match("ZAA", stringInput) then
print("I matched: ZAA")
elseif string.match("AA", stringInput) then
print("I matched: AA")
end
If you want an exact match, just use ==.
if stringInput == 'ZAA' then
print('I matched: ZAA')
elseif stringInput == 'AA' then
print('I matched: AA')
end
From the Lua 5.3. Reference Manual: string.match
string.match (s, pattern [, init])
Looks for the first match of pattern (see §6.4.1) in the string s. If it finds one, then match
returns the captures from the pattern; otherwise it returns nil. If
pattern specifies no captures, then the whole match is returned. A
third, optional numeric argument init specifies where to start the
search; its default value is 1 and can be negative.
so
local inputString = "AA"
string.match("ZAA", inputString)
will match because "AA" is in "ZAA".
You confused the function arguments s and pattern.
local inputString = "AA"
inputString:match("ZAA")
will not match because the pattern contains more characters than inputString.
But as
local inputString = "ZAA"
will both match
inputString:match("AA") and inputString:match("ZAA") you'll probably have to add more constraints.
Please read the manual!

Break strings into substrings based on delimiters, with empty substrings

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

Reverse string.find() or string.gmatch in Lua?

I have a string that contains something like this:
##### abc 'foo'
/path/to/filename:1
##### abc 'bar'
/path/to/filename:1
The string can potentially be very long (say, 50 lines) and doesn't change often.
I would like to fetch the last occurrence of text in between the single-quotes (bar in this example). This is similar to someone else's Python problem (except the answer there doesn't work for me in Lua, as seen far below).
I could parse each line, and put the results into an array, and then just take the last element of the array, but that doesn't seem elegant to me:
local text = [[
##### abc 'foo'
/path/to/filename:1
##### abc 'bar'
/path/to/filename:1
]]
local arr = {}
local pattern = "abc '([^']+)'"
for s in text:gmatch(pattern) do
table.insert(arr, s)
end
print('last:', arr[#arr])
I'm interested in using Lua string patterns to search the string from the end. The pattern I tried below starts from the beginning instead of the end:
local text = [[
##### abc 'foo'
/path/to/filename:1
##### abc 'bar'
/path/to/filename:1
]]
-- FIXME: pattern searches from beginning
local pattern = "abc '([^']+)'.*$"
local s = text:gmatch(pattern)()
assert(s == 'bar', 'expected "bar" but saw "'..s..'"')
print('last:', s)
This yields:
input:12: expected "bar" but saw "foo"
What string pattern specifies the "reverse search" I'm looking for?
You could use
local pattern = ".*abc '([^']+)'"
The .* is greedy so it chews up as much as it can before it matches (in this case, it chews up all the earlier matches and gives you the last).
Or if you really wanted, you could reverse your string and (sort of) your pattern too, but I think it's better to rely on the greedy .* :P
pattern = "'([^']+)' cba"
print(text:reverse():gmatch(pattern)()) -- rab
print(text:reverse():gmatch(pattern)():reverse()) -- bar
Another option would be to use the $ pattern anchor to anchor the pattern at the end of the string. You also don't need to use gmatch here, just match suffices (and saves you the need to call the iterator function returned by gmatch). All in all you get:
text:match"'([^']+)'$"

Error in string.gsub with backslash

local a = "te\st"
local b = string.gsub(a,'\','\\\\')
assert(false,b)
What am I doing wrong?
When I do assert, I want that to the screen the string te\st will be printed... but it's not working
I have a JSON file, that I want to decode it into Lua table. I don't need to print out nothing, I did the assert just to test a local problem.
So what I need is to keep all data in the JSON file that has '\'.
Use [[]] instead of "" or '' if you don't want backslash to have special meaning.
Read about literal strings in the manual.
Have you tried escaping it with the % character instead of \
I don't know if this will help, but I was having a HELL of a time making Lua's gsub match my string with special characters in it that I wanted treated literally... it turned out that instead of using \ as an escape character, or doubling the character, that I needed to prefix the special character with % to make it be treated literally.
Your question wasn't too clear so I'm not 100% sure what you mean. Do you mean that you want the assert to fire when b is equal to the string "te\st"? If so you can do a simple:
assert(b ~= "te\st")
Or I suppse...
assert(b ~= a)
You don't need the gsub. But here it is anyways.
local a = "te\\st"
local b = string.gsub(a,'\\','\\')
assert(false,b)

How to retrieve value from optional parser in Parsec?

Sorry if it's a novice question - I want to parse something defined by
Exp ::= Mandatory_Part Optional_Part0 Optional_Part1
I thought I could do this:
proc::Parser String
proc = do {
;str<-parserMandatoryPart
;str0<-optional(parserOptionalPart0) --(1)
;str1<-optional(parserOptionalPart1) --(2)
;return str++str0++str1
}
I want to get str0/str1 if optional parts are present, otherwise, str0/str1 would be "".
But (1) and (2) won't work since optional() doesn't allow extracting result from its parameters, in this case, parserOptionalPart0/parserOptionalPart1.
Now What would be the proper way to do it?
Many thanks!
Billy R
The function you're looking for is optionMaybe. It returns Nothing if the parser failed, and returns the content in Just if it consumed input.
From the docs:
option x p tries to apply parser p. If p fails without consuming input, it returns the value x, otherwise the value returned by p.
So you could do:
proc :: Parser String
proc = do
str <- parserMandatoryPart
str0 <- option "" parserOptionalPart0
str1 <- option "" parserOptionalPart1
return (str++str0++str1)
Watch out for the "without consuming input" part. You may need to wrap either or both optional parsers with try.
I've also adjusted your code style to be more standard, and fixed an error on the last line. return isn't a keyword; it's an ordinary function. So return a ++ b is (return a) ++ b, i.e. almost never what you want.

Resources