How to dissect and parse a string in lua? - string

I am trying to make command arguments in Roblox. For example, /kill playername. The problem is I don't know how to parse the playername from the string /kill playername. This code is in something like this:
game:GetService("Players").PlayerAdded:Connect(function(Player)
Player.Chatted:Connect(function(Message)
if string.sub(1, #Message) == "/kill " then
--this means the string starts with /kill and is expecting an argument.
--How can I parse this argument from the string
end
end)
end)
Edit: I want to add /setdata <Playername> <DataToChange eg. money> <Value>
Example command:
/setdata MyRobloxUsername Money 10000
I am trying to use something like this to do so
local Command, Playername, DataToChange, Value = string.match(???)
I just need to get the values from the string into variables. I can figure out how to change the data using the variables myself. Just how to get the values from the string. How can I do what I am describing?
I unaccepted the answer because I need further help. Once I get this help I will re accept it. My next request is similar, but with 3 arguments instead of 1. I need help as string:Match() is very counter intuitive to me

Use string.match:
Message=" /kill playername "
command, arg = Message:match("%s*/(.-)%s+(.*)%s*$")

If you want this to be more flexible to more commands in the future, I suggest you take both lhf's and BotOfWar's suggestions and combine them.
local function executeCommandInMessage(message)
-- do a quick regex of the message to see if it is formatted as a command
-- all we care about is the command, any arguments are optional.
local command, arguments = string.match(message, "^/(%w+)[%s]?([%w%s]+)$")
if command ~= nil then
-- we've found a command, parse the arguments into groups of non-space characters
-- then store each word in the parts array
local parts = {}
for w in arguments:gmatch("%S+") do
table.insert(parts, w)
end
-- handle each command individually
if command == "kill" then
local player = parts[1]
print(string.format("Killing %s", player))
elseif command == "setdata" then
local player = parts[1]
local value = parts[2]
local amount = parts[3]
print(string.format("Setting %s on %s to %s", value, player, amount))
-- add any further commands to the list..
-- elseif command == "" then
end
end
end
-- listen for any message submitted by players
game:GetService("Players").PlayerAdded:Connect(function(Player)
Player.Chatted:Connect(function(msg)
-- check for any commands
executeCommandInMessage(msg)
end)
end)
In the future, if you need a better regex to parse the message, I suggest you take a look at how to do Lua pattern matching. They're pretty easy to read once you know what to look at.

I suggest splitting the string with the string.split method to get the segments, then check if the first value is what you want.
game:GetService("Players").PlayerAdded:Connect(function(Player)
Player.Chatted:Connect(function(Message)
local segments = Message:split(" ")
if((#segments >= 1) and (segments[1] == "/kill")) then
-- The rest of the arguments can be accessed like this:
local args = {unpack(segments, 2)} -- Gets every argument after the first value,
-- which is the command.
end
end)
end)

Related

String manipulation overlapping

def Codehelp(st):
i = 0
noot = ""
while i < len(st):
if st[i] == '$':
noot += "**" + st[i] + "**"
i += 1
if '$' in noot:
return noot
else:
return "**"
return
Basically, every time user inputs a string that has '$' in it, the code will return only the '$' with 2 asterisks before and after the '$'. In most test cases the code runs fine, however once inputting a string with more than one '$', i.e. Codehelp("r$t$$"), it outputs **$****$****$** (add two * to both the beginning and end of the string), instead of ****$**$** (ignore the first two *).
Well like you say I get this output: **$****$****$**.
I can certainly say that this makes sense because it only checks on the current instance of $ and adds ** before and after.
If you want it to be more "intelligent", you might need to implement something that might peek ahead to see if there is another $ after the current one.
However, to get **$$ (not sure it that is what you expect, please correct me), you need to look at the next index to see if there is another $ (obviously taking string length boundary checks into consideration as well) and not add additional ** after the second $.

How to validate this string when we don't have the `|` operator in Lua?

I have strings of the form:
cake!apple!
apple!
cake!juice!apple!cake!
juice!cake!
In other words, these strings are composed of the three sub-strings "cake!", "apple!" and "juice!".
I need to validate these strings. The way to do this with a regular expression is thus:
/^(apple!|juice!|cake!)*$/
But Lua's patterns don't have the | operator, so it seemingly can't be done this way.
How can I validate my strings in Lua?
(I don't care about the contents of the strings: I only care about whether they conform (validate) or not.)
I know to write the code to do this but I can't think of a short way to do this. I'm looking for a short solution. I wonder if there's an elegant solution that I'm not aware of. Any ideas?
if str:gsub("%w+!", {["apple!"]="", ["juice!"]="", ["cake!"]=""}) == "" then
--do something
end
This solution uses a table as the second parameter to string.gsub. Since the patterns all match %w+, the table will validate for second time, only the real three patterns are replaced with an empty string. If after all the replacement, the string becomes empty, then the match succeeds.
Using a helper table variable can make it more clear:
local t = {["apple!"]="", ["juice!"]="", ["cake!"]=""}
if str:gsub("%w+!", t) == "" then
--do something
end
If there is a character that will never be in your string, for instance, the character "\1"(ASCII 1) is unlikely in a normal string, you can use this:
local str = "cake!juice!apple!cake!"
if str:gsub("apple!","\1"):gsub("juice!","\1"):gsub("cake!","\1"):gsub("\1","") == "" then
--do something
end
By replacing every match of the patterns to "\1", and finally replace "\1" to an empty string, the correct match would be an empty string in the end.
It has flaws(sometimes it's impossible to find a character that is never in the string), but I think it works in many situations.
The following seems to work for (the included) quick tests.
local strs = {
"cake!apple!",
"bad",
"apple!",
"apple!bad",
" apple!bad",
"cake!juice!apple!cake!",
"cake!juice! apple!cake!",
"cake!juice!badapple!cake!",
"juice!cake!",
"badjuice!cake!",
}
local legalwords = {
["cake!"] = true,
["apple!"] = true,
["juice!"] = true,
}
local function str_valid(str)
local newpos = 1
for pos, m in str:gmatch("()([^!]+!)") do
if not legalwords[m] then
return
end
newpos = pos + m:len()
end
if newpos ~= (str:len() + 1) then
return nil
end
return true
end
for _, str in ipairs(strs) do
if str_valid(str) then
print("Match: "..str)
else
print("Did not match: "..str)
end
end
Just to provide another answer, you can do this easily with lpeg's re module:
re = require 're'
local testdata =
{
"cake!apple!",
"apple!",
"cake!juice!apple!cake!",
"cake!juice!badbeef!apple!cake!",
"juice!cake!",
"badfood",
}
for _, each in ipairs(testdata) do
print(re.match(each, "('cake!' / 'apple!' / 'juice!')*") == #each + 1)
end
This outputs:
true
true
true
false
true
false
This looks almost like your regex pattern above minus the ^ $ of course since lpeg matching is always anchored.
Lua patterns are not a replacement for regular expressions, and cannot represent this sort of pattern. In this case, you just need to repeatedly make sure the front of the string matches one of your words and then pop it off, but you probably already knew that.
Something like:
local words = {cake=1,apple=2,juice=3}
local totals = {}
local matches = 0
local invalid = 0
string.gsub("cake!","(%a+)!",
function(word)
local index = words[word]
if index then
matches = matches + 1
totals[index] = totals[index] + 1
else
invalid = invalid + 1
end
end
)
if matches > 0 and invalid == 0 then
-- Do stuff
end
This will pass each word to the supplied function where you can validate each one.
I dont know if it'll help you to get by you problem. But using string.find() i could use "or". look:
str="juice!"
print(string.find(str, "cake!" or "teste"))
best regards

How do I remove lines from a string begins with specific string in Lua?

How do I remove lines from a string begins with another string in Lua ? For instance i want to remove all line from string result begins with the word <Table. This is the code I've written so far:
for line in result:gmatch"<Table [^\n]*" do line = "" end
string.gmtach is used to get all occurrences of a pattern. For replacing certain pattern, you need to use string.gsub.
Another problem is your pattern <Table [^\n]* will match all line containing the word <Table, not just begins with it.
Lua pattern doesn't support beginning of line anchor, this almost works:
local str = result:gsub("\n<Table [^\n]*", "")
except that it will miss on the first line. My solution is using a second run to test the first line:
local str1 = result:gsub("\n<Table [^\n]*", "")
local str2 = str1:gsub("^<Table [^\n]*\n", "")
The LPEG library is perfect
for this kind of task.
Just write a function to create custom line strippers:
local mk_striplines
do
local lpeg = require "lpeg"
local P = lpeg.P
local Cs = lpeg.Cs
local lpegmatch = lpeg.match
local eol = P"\n\r" + P"\r\n" + P"\n" + P"\t"
local eof = P(-1)
local linerest = (1 - eol)^1 * (eol + eof) + eol
mk_striplines = function (pat)
pat = P (pat)
local matchline = pat * linerest
local striplines = Cs (((matchline / "") + linerest)^1)
return function (str)
return lpegmatch (striplines, str)
end
end
end
Note that the argument to mk_striplines() may be a string or a
pattern.
Thus the result is very flexible:
mk_striplines (P"<Table" + P"</Table>") would create a stripper
that drops lines with two different patterns.
mk_striplines (P"x" * P"y"^0) drops each line starting with an
x followed by any number of y’s -- you get the idea.
Usage example:
local linestripper = mk_striplines "foo"
local test = [[
foo lorem ipsum
bar baz
buzz
foo bar
xyzzy
]]
print (linestripper (test))
The other answers provide good solutions to actually stripping lines from a string, but don't address why your code is failing to do that.
Reformatting for clarity, you wrote:
for line in result:gmatch"<Table [^\n]*" do
line = ""
end
The first part is a reasonable way to iterate over result and extract all spans of text that begin with <Table and continue up to but not including the next newline character. The iterator returned by gmatch returns a copy of the matching text on each call, and the local variable line holds that copy for the body of the for loop.
Since the matching text is copied to line, changes made to line are not and cannot modifying the actual text stored in result.
This is due to a more fundamental property of Lua strings. All strings in Lua are immutable. Once stored, they cannot be changed. Variables holding strings are actually holding a pointer into the internal table of reference counted immutable strings, which permits only two operations: internalization of a new string, and deletion of an internalized string with no remaining references.
So any approach to editing the content of the string stored in result is going to require the creation of an entirely new string. Where string.gmatch provides an iteration over the content but cannot allow it to be changed, string.gsub provides for creation of a new string where all text matching a pattern has been replaced by something new. But even string.gsub is not changing the immutable source text; it is creating a new immutable string that is a copy of the old with substitutions made.
Using gsub could be as simple as this:
result = result:gsub("<Table [^\n]*", "")
but that will disclose other defects in the pattern itself. First, and most obviously, nothing requires that the pattern match at only the beginning of the line. Second, the pattern does not include the newline, so it will leave the line present but empty.
All of that can be refined by careful and clever use of the pattern library. But it doesn't change the fact that you are starting with XML text and are not handling it with XML aware tools. In that case, any approach based on pattern matching or even regular expressions is likely to end in tears.
result = result:gsub('%f[^\n%z]<Table [^\n]*', '')
The start of this pattern, '%f[^\n%z], is a frontier pattern which will match any transition from either a newline or zero character to another character, and for frontier patterns the pre-first character counts as a zero character. In other words, using that prefix allows the rest of the pattern to match at either the first line or any other start-of-line.
Reference: the Lua 5.3 manual, section 6.4.1 on string patterns

Conditionals for data type in gnuplot functions

I would like to define a function which returns the string "NaN" or sprintf("%g",val) depending on whether val is a string or a numeric value. Initially I was trying to test if val was defined (using the gnuplot "exists" function) but it seems that I cannot pass any undefined variable to a function (an error is issued before the function is evaluated). Therefore: is there a way to test inside a function whether the argument is a string or numeric?
I search for a function isstring which I can use somehow like
myfunc(val)=(isstring(val)?"NaN":sprintf("%g",val))
The goal is to output the values of variables without risking errors in case they are undefined. However I need it as a function if I want a compact code for many variables.
Gnuplot doesn't really have the introspection abilities that many other languages have. In fact, it treats strings and numbers (at least integers) very similarly:
print "1"+2 #prints 3
a=1
print "foo".a #prints foo1
I'm not exactly sure how this is implemented internally. However, what you're asking is very tricky to get to work.
Actually, I think your first attempt (checking if a variable exists) is more sensible as type-checking in gnuplot is impossible*. You can pass the variable name to the function as a string, but the problem is that you don't seem to have a handle on the value. All seems lost -- But wait, gnuplot has an eval statement which when given a string will evaluate it. This seems great! Unfortunately, it's a statement, not a function (so it can't be used in a function -- argv!). The best solution I can come up with is to write a function which returns an expression that can be evaluated using eval. Here goes:
def exists_func(result,var)=sprintf("%s=exists('%s')?sprintf('%g',var):'NaN'",result,var,var)
Now when you want to use it, you just prefix it with eval
a=3
eval exists_func("my_true_result","a")
print my_true_result #3
eval exists_func("my_false_result","b")
print my_false_result #NaN
This goes against the grain a little bit. In most programming languages, you'd probably want to do something like this:
my_true_result=exists_func(a)
But alas, I can't figure out how to make that form work.
Of course, the same thing goes here that always goes with eval. Don't use this function with untrusted strings.
*I don't actually know that it's impossible, but I've never been able to get it to work
EDIT
In response to your comment above on the question, I think a function like this would be a little more intuitive:
def fmt(x)=(x==x)?sprintf("%g",x):"NaN"
With this function, your "sentinal/default" value should be NaN instead of "undefined", but it doesn't seem like this should make too much of a difference...(Really, if you're willing to live with "nan" instead of "NaN" you don't need this function at all -- sprintf will do just fine. (Note that this works because according to IEEE, NaN doesn't equal anything (even itself)).
You helped me a lot these days with gnuplot. I want to give you something back because I have found a solution to check if a variable is numeric or not. This helps to decide which operators can be used on it (e.g. == for numbers, eq for strings).
The solution is not very simple, but it works. It redirects gnuplot's print command to a temp file, writes the variable to the file with print myvar and evaluates the file's first line with system("perl -e '<isnumeric(line#1 in temp file)?>' ") (<> is pseudo-code). Let me know if there's room for imrpovements and let me hear your suggestions!
Example: myvar is a float. Any integer (1 or "1") or string value ("*") works too!
myvar = -2.555
# create temporary file for checking if variables are numeric
Int_tmpfle = "tmp_isnumeric_check"
# redirect print output into temp file (existing file is overwritten)
set print Int_tmpfle
# save variable's value to file
print myvar
# check if file is numeric with Perl's 'looks_like_number' function
isnumeric = system("perl -e 'use Scalar::Util qw(looks_like_number); \
open(FLE,".Int_tmpfle."); $line = < FLE >; \
if (looks_like_number($line) > 0) {print qq(y)} ' ")
# reset print output to < STDOUT> (terminal)
set print "-"
# make sure to use "," when printing string and numeric values
if (isnumeric eq "y") {print myvar," is numeric."} else {print myvar," is not numeric."}

Lua string.match problem?

how can I match following strings with one expression?
local a = "[a 1.001523] <1.7 | [...]> < a123 > < ? 0 ?>";
local b = "[b 2.68] <..>";
local c = "[b 2.68] <>";
local d = "[b 2.68] <> < > < ?>";
local name, netTime, argument1, argument2, argumentX = string:match(?);
-- (string is a or b or c or d)
The problem is, the strings can have various counts of arguments( "<...>" ) and the arguments can have numbers, chars, special chars or spaces in it.
I'm new to Lua and I have to learn string matching, but I cannot learn this in a few hours. I ask YOU, because I need the result tomorrow and I really would appreciate your help!
cheers :)
Lua patterns are very limited, you can't have alternative expressions and no optional groups. So that means all of your arguments would need to be matched with the same expressions and you would need to use a fixed amount of arguments if you only write a single pattern. Check this tutorial, it doesn't take long to get used to lua patterns.
You might be still able to parse those strings using multiple patterns. ^%[(%a+)%s(%d+%.%d+)%]%s is the best you can do to get the first part, assuming local name can have multiple upper and lower case letters. To match the arguments, run multiple patterns on parts of the input, like <%s*> or <(%w+)> to check each argument individually.
Alternatively get a regex library or a parser, which would be much more useful here.
Lua patterns are indeed limited, but you can get around if you can make some assumptions. Like if there will be no >'s in the arguments you could just loop over all matching pairs of <> :
local a = "[a 1.001523] <1.7 | [...]> < a123 > < ? 0 ?>"
local b = "[b 2.68] <..>"
local c = "[b 2.68] <>"
local d = "[b 2.68] <> < > < ?>"
function parse(str)
local name,nettime,lastPos = str:match'%[(%a+)%s(%d+%.%d+)%]()'
local arguments={}
-- start looking for arguments only after the initial part in [ ]
for argument in str:sub(lastPos+1):gmatch('(%b<>)') do
argument=argument:sub(2,-2) -- strip <>
-- do whatever you need with the argument. Here we'll just put it in a table
arguments[#arguments+1]=argument
end
return name,nettime,unpack(arguments)
end
For more complicated things you'll be better of using something like LPEG, like kapep said.

Resources