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

[Question] String separation

Started by Cranium, 11 September 2012 - 07:37 PM
Cranium #1
Posted 11 September 2012 - 09:37 PM
I'm trying to figure out how to separate my strings into a command, based off of what the user inputs. I know I'm supposed to use string.match, but I don't know the syntax…

--i need my code here:
local input = read()
command, user, message = input --i need this to be separated into these three variables by default
--but if there is no command, which is defined by the preceding "/", then just send the message
if command == "/register" then

[indent=1]rednet.send(hostID,"/register"..user..","..message)[/indent]
elseif command == "/msg" then
[indent=1]rednet.send(hostID,"/msg"..user..","..message)[/indent]
end
icehaunter #2
Posted 11 September 2012 - 10:11 PM
I didn't quite get the part with three variables.

There is a basic solution if you want to separate messages and commands:

if string.sub(input, 1, 1) == "/" then command = input
else message = input
end
Cranium #3
Posted 11 September 2012 - 10:28 PM
I need the input to work kinda like an IRC command. So normally, you would just type your statements, otherwise, if you precede it with a command like /msg, it would take the next word as the user to send to, and then the remaining string as the message. Same thing for /register, where you would do /register username, and it would send that command to the host to register your username.
usage would be like this:

/register Cranium
/msg icehaunter This is my message
icehaunter #4
Posted 11 September 2012 - 11:43 PM
Hmm. I understand. Where is kinda workaround:

input = io.read()

if string.sub(input,1,1) == "/" then
    local command = string.sub(input,2) --this will get you whole command
    if string.find(command, "register") then
        name = string.sub(2 + "register":len) --getting eveything after command
end
end
Repeat for every command) Sorry for not writing full example - from tablet now:)
icehaunter #5
Posted 11 September 2012 - 11:57 PM
Oooh, you can make it much easier by using a little of regex:


input = io.read()

if string.sub(input,1,1) == "/" then
    command = string.match(input, "([^s]+)", 2) --this will basically get everything before the space
    arg1 = string.match(input, "([^s]+)", string.len(command) + 2) --get first argument for command (2 is "/" and first space
    if string.len(input) ~= string.len(command..arg1.."ll") then
        arg2 = string.match(input, "([^s]+)", string.len(command..arg1) + 3)
    end
--you can do as much args as you want the same way
--do whatever you want with command etc.
else message = input
end
Cranium #6
Posted 12 September 2012 - 12:03 AM
Hmm. I understand. Where is kinda workaround:

input = io.read()

if string.sub(input,1,1) == "/" then
	local command = string.sub(input,2) --this will get you whole command
	if string.find(command, "register") then
		name = string.sub(2 + "register":len) --getting eveything after command
end
end
Repeat for every command) Sorry for not writing full example - from tablet now:)
This comment is easy to understand.
Oooh, you can make it much easier by using a little of regex:


input = io.read()

if string.sub(input,1,1) == "/" then
	command = string.match(input, "([^s]+)", 2) --this will basically get everything before the space
	arg1 = string.match(input, "([^s]+)", string.len(command) + 2) --get first argument for command (2 is "/" and first space
	if string.len(input) ~= string.len(command..arg1.."ll") then
		arg2 = string.match(input, "([^s]+)", string.len(command..arg1) + 3)
	end
--you can do as much args as you want the same way
--do whatever you want with command etc.
else message = input
end
This comment made my head explode. You have to clean up the messy bits of goo on my office chair…
Now I do still need the first two words to possibly be "commands" The first word if preceded by / to be the actual command, the second word the variable, and then the rest the string message. Does this code do that? Like I said, your code made my head explode into gooey chunks… :)/>/>
icehaunter #7
Posted 12 September 2012 - 12:12 AM
Yes.
In example you would enter: "/msg Cranium hey"
My code will:
first, understand that it is a command.
secind, read from slash to space (which is "msg") and write it into the "command" variable
third, read from space to space (which is "Cranium") and write it into the "arg1" variable
fourth, check if there is something after first argument (true)
and last, read to the end, writing "hey" into "arg2"

Easy)
Lyqyd #8
Posted 12 September 2012 - 01:03 AM

if string.sub(message, 1, 1) == "/" then
	--is a command
	local command = string.match(message, "/(%a+) ")
	--get the letters immediately following the slash, stopping at the first space.
	local commandArgs = {}
	--a new table for the arguments of this command.
	local comString = string.match(message, "/%a+ (.*)")
	--pull everything after the command itself into another variable (probably an unnecessary step)
	for match in string.gmatch(comString, "[^ t]+") do
	--for each length of non-whitespace,
		table.insert( commandArgs, match )
		--add that length of non-whitespace as a new entry in the commandArgs table.
	end

This is only a code fragment. As you can see, it's part of an incomplete if statement.
Cranium #9
Posted 12 September 2012 - 02:14 AM
So if I were to want to pull out the /register Cranium, and I needed Cranium to be a variable separate that is sent, I would call back to what, exactly? I see that you separated anything directly after the command into a variable comString. Do I use that? Because I am going to use rednet.send(id,command..","..user)
If that is the case, I would also use rednet.send(id,command..","..user..","..message), right? But wouldn't that just send the duplicated variables using message as the send? I thought this would be easy, but I guess I'm wrong.
Not sure how to implement this exactly. I do understand the application of what you have, not the actual usage though. I really need to take some programming classes sometime… :)/>/>
Lyqyd #10
Posted 12 September 2012 - 03:33 AM
This is actually from an IRC-like server program. The server has received a text input from a client (in message) and is separating out the string by spaces, exactly as it was typed. The client just sends whatever was input in this case.

The idea behind having the client send whatever is input without trying to figure any of it out is that except for a very few commands (/quit for example), the only thing the client even needs to know is whatever the server sends it. If the server has to split the command anyway, why should the client do so as well, especially when it would just be mucking it back together to send it?
Grim Reaper #11
Posted 12 September 2012 - 04:06 AM
This code separates the first character, being the command trigger (!, /, ., etc.), and then gets all arguments passed.

If I entered:
/msg Cranium Hey there!

The command table returned would look like this:
t[1] = "msg"
t[2] = "Cranium"
t[3] = "Hey there!"

So, it only works with 3 commands.


-- Written by PaymentOption --
function SplitCommand( sCommand )
local tCommands = {}
sCommand = sCommand:sub( 2, string.len( sCommand ) )

-- If the command takes 3 arguments, then we can split the first 3 spaces.
local nSpaceCount = 0
for sMatch in sCommand:gmatch( "%w+" ) do
  nSpaceCount = nSpaceCount + 1
 
  -- Get the rest of the arguments if there are more than 2.
  if nSpaceCount == 3 then
   _, pos2 = sCommand:find( tCommands[1] .. " " .. tCommands[2] )
   tCommands[3] = sCommand:sub( pos2 + 1, string.len( sCommand ) )
   break
  else
   tCommands[#tCommands+1] = sMatch
  end
end

return tCommands
end
Cranium #12
Posted 12 September 2012 - 03:36 PM
So if I got both of you guys right, I should only be having my host be separating the messages out, right? I definitely see where that makes sense. I'll try both of these, and see which one will work out best for me. Thanks for all the help!
This code separates the first character, being the command trigger (!, /, ., etc.), and then gets all arguments passed.

If I entered:
/msg Cranium Hey there!

The command table returned would look like this:
t[1] = "msg"
t[2] = "Cranium"
t[3] = "Hey there!"

So, it only works with 3 commands.


-- Written by PaymentOption --
function SplitCommand( sCommand )
local tCommands = {}
sCommand = sCommand:sub( 2, string.len( sCommand ) )

-- If the command takes 3 arguments, then we can split the first 3 spaces.
local nSpaceCount = 0
for sMatch in sCommand:gmatch( "%w+" ) do
  nSpaceCount = nSpaceCount + 1

  -- Get the rest of the arguments if there are more than 2.
  if nSpaceCount == 3 then
   _, pos2 = sCommand:find( tCommands[1] .. " " .. tCommands[2] )
   tCommands[3] = sCommand:sub( pos2 + 1, string.len( sCommand ) )
   break
  else
   tCommands[#tCommands+1] = sMatch
  end
end

return tCommands
end
Just to make sure, since this is a function, I call to it like this:

local msgArgs = SplitCommand(message)
And it would return a table of arguments, right? Just like you said at the top of your comment?
Grim Reaper #13
Posted 12 September 2012 - 11:23 PM
Yes.