Page 1 of 1

another problem with string pattern

Posted: Mon Apr 04, 2022 8:00 am
by Kavaline
Hi, I need help to find the pattern that have the wanted print output:

Code: Select all

str = "(1, 'test', none, 10, '', , 'hi, this is a default bio. change it (asap)!')"
for inf = 1, 9 do
	pat = "%(" .. string.rep("%s?'?.-'?[,%)]+", inf - 1) .. "%s?'?(.-)'?[,%)]+"
	result = str:match(pat)
	print(result, "#" .. inf)
end
--Print result must be:
--1
--test
--none
--10
--
-- or nil
--hi, this is a default bio. change it (asap)!
--nil
--nil
The actual print result is:
1 #1
test #2
none #3
10 #4
#5
#6
hi #7
this is a default bio. change it (asap #8
! #9

I can't find a solution that if found a starting "'", ignores all rules until find a closing "'" and match a right pattern (a ", " or ")$")

Re: another problem with string pattern

Posted: Mon Apr 04, 2022 10:58 am
by ReFreezed
You cannot do this with a single pattern. You need different patterns for matching quoted and unquoted values. This also means you have to match one item at a time and choose the appropriate pattern dynamically.

Re: another problem with string pattern

Posted: Mon Apr 04, 2022 4:41 pm
by Kavaline
Ouch.. So, I'm thinking in something with another loop inside, counting the quotes to choose what pattern add to 'pat', I will scratch it later

Re: another problem with string pattern

Posted: Mon Apr 04, 2022 11:32 pm
by ReFreezed
No inner loops, counting of quotes, or generation of any pattern needed.

Code: Select all

local str = "(1, 'test', none, 10, '', , 'hi, this is a default bio. change it (asap)!')"

local items = {}
local pos   = str:match("%(%s*()") or error("missing '('")

while true do
	-- Match item.
	local item
	if str:sub(pos, pos) == "'" then
		item, pos = str:match("^'(.-)'%s*()", pos) -- Quoted value.
		assert(item, "missing end quote")
	else
		item, pos = str:match("^([^,)]*)()", pos) -- Unquoted value.
		item      = item:gsub("%s+$", "") -- Trim trailing spaces.
	end

	table.insert(items, item)

	-- Continue or stop.
	if str:sub(pos, pos) == "," then
		pos = str:match("^,%s*()", pos) -- Continue loop.
	elseif str:sub(pos, pos) == ")" then
		break -- End of items.
	else
		error("expected ',' or ')' at position "..pos)
	end
end

for i, item in ipairs(items) do
	print("#"..i.." '"..item.."'")
end

--[[ Output:
#1 '1'
#2 'test'
#3 'none'
#4 '10'
#5 ''
#6 ''
#7 'hi, this is a default bio. change it (asap)!'
]]

Re: another problem with string pattern

Posted: Tue Apr 05, 2022 3:00 am
by Kavaline
But thanks to your direction, I did with them xD
Also, I will check your solution too, I didn't realize that I could use the () to get a string position, I tried something like this, but walked on other way because I couldn't find how to get the right positions. The way was, break the string after every match...

Code: Select all

str = "(1, 'test', none, 10, '', , 'hi, this is a default bio. change it (asap)!')"
s = str
result = {}
for inf = 1, 9 do
	s = string.sub(s or "", math.min(2,s:len()))
	if s:match("^'") then
		result[inf] = s:match("%s?'(.-)'[,%)]+")
	else
		result[inf] = s:match("%s?(.-)[,%)]+")
	end
	s = s:match("^'?" .. string.rep(".", string.len(result[inf] or "")) .. "'?.?(.+)")
	print(result[inf], "#" .. inf)
end
The only problem is that I can't get the exact info that I want; on my first try, I could get a Nth info directly, without a loop (thanks to string.rep). In this solutions, I always will read all infos in a loop, and after, get the one that I want. But if is the fastest way (and less headache), is this

Re: another problem with string pattern

Posted: Wed Apr 06, 2022 5:16 am
by Rigachupe
Or use a JSON library. The input string would change to:
local str = "{1, 'test', none, 10, '', , 'hi, this is a default bio. change it (asap)!'}"

And the output from JSON would be a table that you can traverse like an array.