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

string.sub() isn't returning what I am thinking.

Started by CCJJSax, 07 December 2013 - 01:27 PM
CCJJSax #1
Posted 07 December 2013 - 02:27 PM
Obviously I'm doing something wrong, probably something really simple. I'm thinking I'm having an extended blonde moment. In case you're wondering, I'm making a personal assistant that logs all the items in Applied Energistics into a readable name, such as sand from my current code below. all items from unleashed… huge task I know lol

Spoilerline = ["Sand"] = getAEId(12, 0), "Sand", "Sand"


function getAEId(id, meta)
   return meta*32768 + id
end

function getIdMeta(lineStr)

local firstParenth = string.find(lineStr, "%(")
print(firstParenth)
local secondParenth = string.find(lineStr, "%)")
print(secondParenth)

local insideParenth = string.sub(lineStr, firstParenth, secondParenth)
local comma, _ = string.find(insideParenth, ",")
print(insideParenth)
print(comma)
local id = string.sub(insideParenth, firstParenth+1, comma-1)
print(id)
local meta = string.sub(insideParenth, comma, #insideParenth)
print(meta)
print(getAEId(id, meta))

end

for line in io.open("itemListTemp.lua"):lines() do -- Line is shown above. ignore the for loop.
  if string.find(string.lower(line), "sand") then
	print(line)
	getIdMeta(tostring(line))
  end
end

returns this
["Sand"] = getAEId(12, 0), "Sand", "Sand"
19
25
(12, 0) – this should equal 12
22 – this should be 3 (place of comma in insideParenth)
(12, 0) – this should equal 0

then errors on line 2, but I think I can handle that.
Edited on 08 December 2013 - 12:05 AM
Shazz #2
Posted 07 December 2013 - 03:38 PM
Both string.sub and string.find work on their own, but calling string.find on a string returned by string.sub will return the index of the character in the original string, not the sub string you are referencing. […] Concatenating a blank string onto the end of the string causes new memory to be allocated and works properly again

http://computercraft.info/wiki/String_%28API%29
Engineer #3
Posted 07 December 2013 - 03:44 PM
Okay, I dont really see what you are doing, but patterns are obviously the way to go. Read upon them.
Assuming the input is "(1,1)" where 1 can be different numbers.

local function getIdMeta( s )
	local id = s:gsub( "%((%d-),%d-%)", "%1" )
	local meta = s:gsub( "%(%d-,(%d-)%)", "%1" )
	if tonumber(id) and tonumber(meta) then
		-- We got the id and meta
	end
end
Though this is untested and Im abviously not the best with patterns, but I think it does what it is supposed to do.
Edited on 07 December 2013 - 03:23 PM
Symmetryc #4
Posted 07 December 2013 - 04:39 PM
Okay, I dont really see what you are doing, but patterns are obviously the way to go. Read upon them.
Assuming the input is "(1,1)" where 1 can be different numbers.

local function getIdMeta( s )
	local id = s:gsub( "%((%d-),%d-%)", "%1" )
	local meta = s:gsub( "%(%d-,(%d-)%)", "%1" )
	if tonumber(id) and tonumber(meta) then
		-- We got the id and meta
	end
end
Though this is untested and Im abviously not the best with patterns, but I think it does what it is supposed to do.
Your code works, but the way your using gsub isn't really how it's supposed to be used, try using this:

local getIdMeta = function(s)
        local id, meta = s:match("%((%d-),(%d-)%)")
        if tonumber(id) and tonumber(meta) then
                -- We got the id and meta
        end
end
it uses s:match instead which makes it much cleaner as string.match was meant for this kind of stuff.
Edited on 07 December 2013 - 03:40 PM
Engineer #5
Posted 07 December 2013 - 05:55 PM
Your code works, but the way your using gsub isn't really how it's supposed to be used, try using this:

local getIdMeta = function(s)
		local id, meta = s:match("%((%d-),(%d-)%)")
		if tonumber(id) and tonumber(meta) then
				-- We got the id and meta
		end
end
it uses s:match instead which makes it much cleaner as string.match was meant for this kind of stuff.
Even more proof I dont use patterns much :P/> or lua in general
Edited on 07 December 2013 - 04:58 PM
CCJJSax #6
Posted 08 December 2013 - 12:02 AM
Okay, I dont really see what you are doing, but patterns are obviously the way to go. Read upon them.
Assuming the input is "(1,1)" where 1 can be different numbers.

local function getIdMeta( s )
	local id = s:gsub( "%((%d-),%d-%)", "%1" )
	local meta = s:gsub( "%(%d-,(%d-)%)", "%1" )
	if tonumber(id) and tonumber(meta) then
		-- We got the id and meta
	end
end
Though this is untested and Im abviously not the best with patterns, but I think it does what it is supposed to do.
Your code works, but the way your using gsub isn't really how it's supposed to be used, try using this:

local getIdMeta = function(s)
		local id, meta = s:match("%((%d-),(%d-)%)")
		if tonumber(id) and tonumber(meta) then
				-- We got the id and meta
		end
end
it uses s:match instead which makes it much cleaner as string.match was meant for this kind of stuff.

Part of me doesn't want to use this because I have no idea what's going on. I need to look into this. The more I know about programming, the more I don't know it would seem lol

I'm taking a shot in the dark guess of what that means.

("%((%d-), (%d-)%)") – outer parentheses probably just wrap this all together, and the quotes turn it into a string.
%((%d-), (%d-)%) – the %( and the %) are searching for parenthases
(%d-), (%d-) – the first of these two probably search for what happens before the comma, the last for after the comma
, – the comma mentioned in the last line of my guess?

is that close?

finally does that work if the numbers are like (15000, 12)?
Edited on 07 December 2013 - 11:09 PM
CCJJSax #7
Posted 08 December 2013 - 12:22 AM
with that code both id and meta are returning nil.
Kingdaro #8
Posted 08 December 2013 - 12:28 AM
Yeah, patterns can be pretty confusing at first, but are easily understood the more you work with them.

In general, the concept in lua patterns mainly revolves around the use of the % character, where whatever comes after it determines the kind of "character set" you're going to be matching. For example, %d matches all digits, %a matches all letters, %l matches lowercase letters, and so on. Anything that isn't a special pattern matching identifier matches the symbol itself, e.g. %+ would match a plus sign (this is useful since the plus sign is used in patterns themselves).

Example:

print(string.match(str, "%d"))

If str were "123", this code would print "1", because the code only tries to find one digit. If we wanted multiple digits, we would use a modifier, such as the aformentioned plus sign, which finds the longest match of a certain set.


print(string.match(str, "%d+"))

If str were "123", this would print "123". If it were "12.3", it would print "12", since the period in that string is not a digit character. I would give a more in-depth explanation, but that would take too long.

As for your problem at hand, symmetric's solution would work, had it accounted for various forms of spacing. "%s" matches any space character: a space, a tab, or a newline. If we add in %s* in that matchstring in various places to account for spacing in those places:


local getIdMeta = function(s)
   local id, meta = s:match("%(%s*(%d-)%s*,%s*(%d-)%s*%)") --# this line right here
   if tonumber(id) and tonumber(meta) then
      -- We got the id and meta
   end
end

The * modifier matches 0 or more characters, meaning there doesn't actually have to be a space in those places. For this example, (1,2) would work, (1, 2) would work, even ( 1, 2).
Symmetryc #9
Posted 08 December 2013 - 12:32 AM
We'll fist off, I'd recommend that you look up "Lua Patterns", it'll give you some nice results on what that stuff is (I would give you a link but I'm on my phone). Your guess was close though. Yes it works for numbers of any size. They are probably returning nil because you are putting a space between the comma and the second number. If you want to make it work with spaces and no spaces then use: "%((%d-),.-(%d-)%)". Hope this helps!

Edit: Crap, Kingdaro you ninja :P/>
Edited on 07 December 2013 - 11:33 PM
CCJJSax #10
Posted 08 December 2013 - 12:38 AM
Yeah, patterns can be pretty confusing at first, but are easily understood the more you work with them.

In general, the concept in lua patterns mainly revolves around the use of the % character, where whatever comes after it determines the kind of "character set" you're going to be matching. For example, %d matches all digits, %a matches all letters, %l matches lowercase letters, and so on. Anything that isn't a special pattern matching identifier matches the symbol itself, e.g. %+ would match a plus sign (this is useful since the plus sign is used in patterns themselves).

Example:

print(string.match(str, "%d"))

If str were "123", this code would print "1", because the code only tries to find one digit. If we wanted multiple digits, we would use a modifier, such as the aformentioned plus sign, which finds the longest match of a certain set.


print(string.match(str, "%d+"))

If str were "123", this would print "123". If it were "12.3", it would print "12", since the period in that string is not a digit character. I would give a more in-depth explanation, but that would take too long.

As for your problem at hand, symmetric's solution would work, had it accounted for various forms of spacing. "%s" matches any space character: a space, a tab, or a newline. If we add in %s* in that matchstring in various places to account for spacing in those places:


local getIdMeta = function(s)
   local id, meta = s:match("%(%s*(%d-)%s*,%s*(%d-)%s*%)") --# this line right here
   if tonumber(id) and tonumber(meta) then
	  -- We got the id and meta
   end
end

The * modifier matches 0 or more characters, meaning there doesn't actually have to be a space in those places. For this example, (1,2) would work, (1, 2) would work, even ( 1, 2).

I think your explanation helped a lot, I understand the code now, and it works! Awesome! post voted up ;)/>

We'll fist off, I'd recommend that you look up "Lua Patterns", it'll give you some nice results on what that stuff is (I would give you a link but I'm on my phone). Your guess was close though. Yes it works for numbers of any size. They are probably returning nil because you are putting a space between the comma and the second number. If you want to make it work with spaces and no spaces then use: "%((%d-),.-(%d-)%)". Hope this helps!

Edit: Crap, Kingdaro you ninja :P/>

Well it's the thought that counts :)/>
CCJJSax #11
Posted 08 December 2013 - 12:57 AM
One thing I'm running into now is that sometimes the thing inside the brackets has a _ in there, so what I came up with now doesn't totally work with them. How do I make it find the third quote in the string? this is what I have so far.


local getIdMeta = function(s)
  if string.sub(s, 1, 2) ~= "--" then
	local id, meta = s:match("%(%s*(%d-)%s*,%s*(%d-)%s*%)")
	local itemName = s:match('"(%a*)"')  -- this needs to find the third quote in the line.
	print(itemName)
	print(id)
	print(meta)
	sleep(1)
  end
  if tonumber(id) and tonumber(meta) then
	getAEId(id, meta)
  else
	print("nope")
  end
end
Edited on 08 December 2013 - 12:02 AM
Lyqyd #12
Posted 08 December 2013 - 02:12 AM
Instead of just %a*, [%a_]*
Engineer #13
Posted 08 December 2013 - 06:36 AM

if tonumber(id) and tonumber(meta) then
        getAEId(id, meta)
else
        print("nope")
end

This code wont wrk because id and meta are still strings but you want numbers. This is the same concept as: x = x + 1 and x + 1. X will in the first case itself plus one, but in the latter case it will just maintain the value it was, plus 1. There is never assignment in the latter case.

The same concept is with tonumber:

id = tonumber(id)
meta = tonumber(meta)
Now we assigned a number value or nil to meta and id.
To finish that piece of code of:

if id and meta then
   thatFunctionWhichNeedsNumberValues( id, meta )
else
   print("nope")
end
CCJJSax #14
Posted 08 December 2013 - 03:51 PM

if tonumber(id) and tonumber(meta) then
		getAEId(id, meta)
else
		print("nope")
end

This code wont wrk because id and meta are still strings but you want numbers. This is the same concept as: x = x + 1 and x + 1. X will in the first case itself plus one, but in the latter case it will just maintain the value it was, plus 1. There is never assignment in the latter case.

The same concept is with tonumber:

id = tonumber(id)
meta = tonumber(meta)
Now we assigned a number value or nil to meta and id.
To finish that piece of code of:

if id and meta then
   thatFunctionWhichNeedsNumberValues( id, meta )
else
   print("nope")
end

This did work. Now I just need to get the third quote in the pattern instead of the first.

Instead of just %a*, [%a_]*

That works, and I could make it do what I want, but that would mean I would have to eventually change all the "_" to " " no big deal, but one more step needed.