This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
BrunoZockt's profile picture

[SOLVED] string.gmatch seems to steal letters

Started by BrunoZockt, 12 July 2018 - 12:05 AM
BrunoZockt #1
Posted 12 July 2018 - 02:05 AM
Hey Guys,

after some time I was finally brave enough to work on my turtle program again but came across an issue I can't figure out myself. The complete program: https://pastebin.com/fptxKN9n
Sorry for the title by the way, I don't know how to put it in a few words.

I basically just want to print out the table variables.ignor.
Spoiler

local variables = {"some irrelevant stuff",
["ignor"] = {"minecraft:stone", "minecraft:cobblestone", "minecraft:dirt", "minecraft:lava", "minecraft:flowing_lava", "minecraft:water", "minecraft:flowing_water"}
But I need to do it with my own custom function so that the words wrap around at the end of the screen.
The custom function, although irrelevant for this thread since it works and I've used it for years:
Spoiler

function printWrapped(tab, xpos, ypos, Space, sep)
  local sep = sep or " "
  local leftSpace = Space
  local newxpos = xpos
  for word = 1, #tab do
	if Space < #tab[word] + #sep then
	  return false
	elseif leftSpace >= #tab[word]+#sep then
	  Sprint(tab[word]..sep, newxpos, ypos)
	  leftSpace = leftSpace - (#tab[word]+#sep)
	  newxpos = newxpos + #tab[word]+#sep
	else
	  ypos = ypos + 1
	  leftSpace = Space
	  Sprint(tab[word]..sep, xpos, ypos)
	  leftSpace = leftSpace - (#tab[word]+#sep)
	  newxpos = xpos + (#tab[word]+#sep)
	end
  end
  return ypos
end

function Sprint(str, xpos, ypos)
  term.setCursorPos(xpos, ypos)
  term.write(str)
end
What is relevant, is that my function only takes a table as input. Now you might think: "What is his problem?? He wants to print a table and thats what his function does, right?" Well, hold on. I also want to remove all the parts of the strings that say "minecraft:". I already have a function that should achieve this, even though it was designed for a different purpose:

function Splitter(inputstr, sep)
  if inputstr == nil then
	return nil
  elseif sep == nil then
	sep = " "
  end
  local t={} ; i=1
  for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
	t[i] = str
	i = i + 1
  end
  return t
end
This function only takes a string as input, which is why I use textutils.serialize on my table before feeding it forward to the Splitter.
My complete command looks like this:

printWrapped(Splitter(textutils.serialize(variables.ignor), '\n  "minecraft:'), 18, 4, w-18)
And somewhere in here seems to be the mistake because instead of a somehow expected result, I get

{ s o , obbl s o , d , l v , low g_l v , w , low g_w , }

I hope you can show me what I did wrong and give a short explanation.
Thanks in advance,
Bruno
Edited on 14 July 2018 - 10:59 PM
KingofGamesYami #2
Posted 12 July 2018 - 02:22 AM
I think string.gsub( str, "minecraft:", "" ) would work much better in this case.
Bomb Bloke #3
Posted 12 July 2018 - 03:17 AM
I hope you can show me what I did wrong and give a short explanation.

You're not matching the literal string "minecraft:", you're matching any of the characters contained within it:

"([^"..sep.."]+)"

Yami's gsub is indeed much simpler.

https://www.lua.org/pil/20.2.html

Also, you're making two separate statements here. One defines "t" as a local, one defines "i" as a global:

local t={} ; i=1

If you wanted to make them both local within the space of a single line, you'd do:

local t, i = {}, 1

http://lua-users.org/wiki/AssignmentTutorial
BrunoZockt #4
Posted 12 July 2018 - 10:38 AM
I hope you can show me what I did wrong and give a short explanation.

You're not matching the literal string "minecraft:", you're matching any of the characters contained within it:

"([^"..sep.."]+)"

Yami's gsub is indeed much simpler.

https://www.lua.org/pil/20.2.html

Also, you're making two separate statements here. One defines "t" as a local, one defines "i" as a global:

local t={} ; i=1

If you wanted to make them both local within the space of a single line, you'd do:

local t, i = {}, 1

http://lua-users.org...ignmentTutorial
Thanks a lot for the explanation. Since my function Splitter is actually supposed to match all the letters contained in the seperator, how would I change that function? Just replace string.gmatch with KingofGamesYami's solution or also remove the loop around it?
I actually knew how declaration of variables works but thanks for pointing out the mistake anyways, it's old code and I would have missed it.
I think string.gsub( str, "minecraft:", "" ) would work much better in this case.
Thank you, will try
Bomb Bloke #5
Posted 12 July 2018 - 01:40 PM
how would I change that function? Just replace string.gmatch with KingofGamesYami's solution or also remove the loop around it?

If you changed your pattern to just "minecraft:" (as opposed to "([^minecraft:]+)"), then it'd probably do what you wanted without further changes. If you switched to using gsub as well, then you could also ditch the loop and make your code simpler.

http://lua-users.org/wiki/StringLibraryTutorial
BrunoZockt #6
Posted 12 July 2018 - 05:21 PM
how would I change that function? Just replace string.gmatch with KingofGamesYami's solution or also remove the loop around it?

If you changed your pattern to just "minecraft:" (as opposed to "([^minecraft:]+)"), then it'd probably do what you wanted without further changes. If you switched to using gsub as well, then you could also ditch the loop and make your code simpler.

http://lua-users.org...LibraryTutorial

OK, thanks again. I got it to work and the Tutorial also gave me a good impression of the "string-thing".
But again I've encountered a problem. I want to place commas after every table value.
I came up with this:

string.gsub(textutils.serialize(variables.ignor), "minecraft:(%a+)", function(w) return w.."," end)
But "_" doesn't seem to be a letter, which results in a wrongfully placed comma "flowing,_water". I unsuccessfully tinkered around a bit to find a pattern that matches a normal "one-word name" like "minecraft:stone" aswell as a "two-or-more-words name" like "minecraft:flowing_water". Any idea?
KingofGamesYami #7
Posted 12 July 2018 - 10:05 PM
You can easily create a custom pattern to match all letters and _. It'd look something like this: "[%a_]"
Bomb Bloke #8
Posted 13 July 2018 - 05:00 AM
It'd be way easier to do your matching within the original, unserialised table, though.
BrunoZockt #9
Posted 14 July 2018 - 06:45 PM
You can easily create a custom pattern to match all letters and _. It'd look something like this: "[%a_]"
I'm sorry, but I tried out a lot of different patterns and they didn't work. Including yours. It's really frustrating but I can't figure it out.
It'd be way easier to do your matching within the original, unserialised table, though.
Could you give me an example how that would work?
KingofGamesYami #10
Posted 14 July 2018 - 07:39 PM
Have you tried "^\"" yet? That will match everything except a double quote.
BrunoZockt #11
Posted 15 July 2018 - 12:57 AM
Have you tried "^\"" yet? That will match everything except a double quote.
No, I hadn't and it doesn't work anyways, but your post ultimately lead to me finding out about * which seems to mean "possibly" and is exactly what I needed.
My pattern that places the commas like I want them is

"minecraft:(%a+_*%a*)"
Thank you all!
Bomb Bloke #12
Posted 15 July 2018 - 02:25 AM
It'd be way easier to do your matching within the original, unserialised table, though.
Could you give me an example how that would work?

local variables = {"some irrelevant stuff",
["ignor"] = {"minecraft:stone", "minecraft:cobblestone", "minecraft:dirt", "minecraft:lava", "minecraft:flowing_lava", "minecraft:water", "minecraft:flowing_water"}}

local shortnames = {}

for i = 1, #variables.ignor do shortnames[i] = variables.ignor[i]:gsub("minecraft:", "") end

printWrapped(shortnames, 18, 4, w-18)