Lua string.find can't find "/" in a reverse way of find, look at the following code:
c="~/abc.123"
print(string.find(c,"/",-1,true))
This always returns "nil"
Please refer to the Lua reference manual.
https://www.lua.org/manual/5.3/
string.find(c,"/",-1,true)
The third parameter of string.find will determin where to start the search.
As you entered -1 you will start at the last character of your string and search forward. Of course you won't find anything that way.
For strings positive indices give a position from the beginning and negative indices give a position from the string's end.
Use 1 to start from the first character. Then you'll find your slash. Alternatively you could use anything <= -8
Please note that you could also write c:find("/",1,true) as a shorter version.
To find the last occurrence of /, use string.find(s,".*/"). The second return value is the position of the last /.
Lua can't do leftwards searches, consider reversing the string first:
function Find_Leftwards(s,m,i)
local b,f = s:reverse():find(m, i)
return #s-f, #s-b
end
string.find cannot be used to do a leftwards search of a string. The init parameter merely sets the position from which to begin a rightwards search:
A third, optional numeric argument init specifies where to start the search; its default value is 1 and can be negative.
And an earlier note from the manual on negative indices:
Indices are allowed to be negative and are interpreted as indexing backwards, from the end of the string. Thus, the last character is at position -1, and so on.
You'll need to roll your own function which searches for an index starting from the right:
local function r_find (str, chr)
local bchar = chr:byte(1)
for i = #str, 1, -1 do
if str:byte(i) == bchar then
return i
end
end
end
print(r_find('~/.config/foo/bar', '/')) --> 14
Or consider using string.match to find the last section:
print(('~/.config/foo/bar'):match('/([^/]+)$')) --> 'bar'
Another option would be to simply split the string into its individual sections, and get the last section.
Related
I want to convert this python code to lua .
for i in range(1000,9999):
if str(i).endswith('9'):
print(i)
I've come this far ,,
for var=1000,9000 then
if tostring(var).endswith('9') then
print (var)
end
end
but I don't know what's the lua equivalent of endswith() is ,,, im writing an nmap script,,
working 1st time with lua so pls let me know if there are any errors ,, on my current code .
The python code is not great, you can get the last digit by using modulo %
# python code using modulo
for i in range(1000,9999):
if i % 10 == 9:
print(i)
This also works in Lua. However Lua includes the last number in the loop, unlike python.
-- lua code to do this
for i=1000, 9998 do
if i % 10 == 9 then
print(i)
end
end
However in both languages you could iterate by 10 each time
for i in range(1009, 9999, 10):
print(i)
for i=9, 9998, 10 do
print(i)
for var = 1000, 9000 do
if string.sub(var, -1) == "9" then
-- do your stuff
end
end
XY-Problem
The X problem of how to best port your code to Lua has been answered by quantumpro already, who optimized it & cleaned it up.
I'll focus on your Y problem:
What's the Lua equivalent of Python endswith?
Calling string functions, OOP-style
In Lua, strings have a metatable that indexes the global string library table. String functions are called using str:func(...) in Lua rather than str.func(...) to pass the string str as first "self" argument (see "Difference between . and : in Lua").
Furthermore, if the argument to the call is a single string, you can omit the parentheses, turning str:func("...") into str:func"...".
Constant suffix: Pattern Matching
Lua provides a more powerful pattern matching function that can be used to check whether a string ends with a suffix: string.match. str.endswith("9") in Python is equivalent to str:match"9$" in Lua: $ anchors the pattern at the end of the string and 9 matches the literal character 9.
Be careful though: This approach doesn't work with arbitrary, possibly variable suffices since certain characters - such as $ - are magic characters in Lua patterns and thus have a special meaning. Consider str.endswith("."); this is not equivalent to string:match".$" in Lua, since . matches any character.
I'd say that this is the lua-esque way of checking whether a string ends with a constant suffix. Note that it does not return a boolean, but rather a match (the suffix, a truthy value) if successful or nil (a falsey value) if unsuccessful; it can thus safely be used in ifs. To convert the result into a boolean, you could use not not string:match"9$".
Variable suffix: Rolling your own
Lua's standard library is very minimalistic; as such, you often need to roll your own functions even for basic things. There are two possible implementations for endswith, one using pattern matching and another one using substrings; the latter approach is preferable because it's shorter, possibly faster (Lua uses a naive pattern matching engine) and doesn't have to take care of pattern escaping:
function string:endswith(suffix)
return self:sub(-#suffix) == suffix
end
Explanation: self:sub(-#suffix) returns the last suffix length characters of self, the first argument. This is compared against the suffix.
You can then call this function using the colon (:) syntax:
str = "prefixsuffix"
assert(str:endswith"suffix")
assert(not str:endswith"prefix")
how can I extract a few words separated by symbols in a string so that nothing is extracted if the symbols change?
for example I wrote this code:
function split(str)
result = {};
for match in string.gmatch(str, "[^%<%|:%,%FS:%>,%s]+" ) do
table.insert(result, match);
end
return result
end
--------------------------Example--------------------------------------------
str = "<busy|MPos:-750.222,900.853,1450.808|FS:2,10>"
my_status={}
status=split(str)
for key, value in pairs(status) do
table.insert(my_status,value)
end
print(my_status[1]) --
print(my_status[2]) --
print(my_status[3]) --
print(my_status[4]) --
print(my_status[5]) --
print(my_status[6]) --
print(my_status[7]) --
output :
busy
MPos
-750.222
900.853
1450.808
2
10
This code works fine, but if the characters and text in the str string change, the extraction is still done, which I do not want to be.
If the string change to
str = "Hello stack overFlow"
Output:
Hello
stack
over
low
nil
nil
nil
In other words, I only want to extract if the string is in this format: "<busy|MPos:-750.222,900.853,1450.808|FS:2,10>"
In lua patterns, you can use captures, which are perfect for things like this. I use something like the following:
--------------------------Example--------------------------------------------
str = "<busy|MPos:-750.222,900.853,1450.808|FS:2,10>"
local status, mpos1, mpos2, mpos3, fs1, fs2 = string.match(str, "%<(%w+)%|MPos:(%--%d+%.%d+),(%--%d+%.%d+),(%--%d+%.%d+)%|FS:(%d+),(%d+)%>")
print(status, mpos1, mpos2, mpos3, fs1, fs2)
I use string.match, not string.gmatch here, because we don't have an arbitrary number of entries (if that is the case, you have to have a different approach). Let's break down the pattern: All captures are surrounded by parantheses () and get returned, so there are as many return values as captures. The individual captures are:
the status flag (or whatever that is): busy is a simple word, so we can use the %w character class (alphanumeric characters, maybe %a, only letters would also do). Then apply the + operator (you already know that one). The + is within the capture
the three numbers for the MPos entry each get (%--%d+%.%d+), which looks weird at first. I use % in front of any non-alphanumeric character, since it turns all magic characters (such as + into normal ones). - is a magic character, so it is required here to match a literal -, but lua allows to put that in front of any non-alphanumerical character, which I do. So the minus is optional, so the capture starts with %-- which is one or zero repetitions (- operator) of a literal - (%-). Then I just match two integers separated by a dot (%d is a digit, %. matches a literal dot). We do this three times, separated by a comma (which I don't escape since I'm sure it is not a magical character).
the last entry (FS) works practically the same as the MPos entry
all entries are separated by |, which I simply match with %|
So putting it together:
start of string: %<
status field: (%w+)
separator: %|
MPos (three numbers): MPos:(%--%d+%.%d+),(%--%d+%.%d+),(%--%d+%.%d+)
separator: %|
FS entry (two integers): FS:(%d+),(%d+)
end of string: %>
With this approach you have the data in local variables with sensible names, which you can then put into a table (for example).
If the match failes (for instance, when you use "Hello stack overFlow"), nil` is returned, which can simply be checked for (you could check any of the local variables, but it is common to check the first one.
This question already has answers here:
Zero-length string being returned from String#split
(2 answers)
Closed 6 years ago.
Why does .split create an empty character when its argument is the first letter of the string, and it doesn't do the same when the argument is the last letter of the string? In the second example, doesn't it "say", since nothing is on my right I'll output "" ? (Is there a 'nil' at the end of the string?)
I know this is not a very relevant question, however, I'd like to understand why the method behaves this way. Thank you!
string = "aware"
string.split("a") --> # outputs: ["", "w", "re"]
string.split("e") --> # outputs: ["awar"]
Below is a simple example of behavioral oddity that String#split may seem to have:
"1234".split(/1/) # => ["", "234"]
It seems like the expected result of the above example would be [“234”] since it is splitting on the 1, but instead we’re getting an unexpected empty string.
**
How String#split works
**
Internally String#split only uses regular expression delimiters. If you pass in a string delimiter it will be escaped for a regular expression and then turned into a regular expression:
1 2 3 4
"1234".split("1") # is really the same as "1234".split( Regexp.new( Regexp.escape("1") ) )
For the remainder of this article when I refer to delimiter I am referring to a regular expression delimiter since internally that is what String#split uses.
String#split keeps track the track of five important pieces of information:
the string itself
a results array which is returned
the position marking where to start matching the string against the
delimiter. This is the start position and is initialized to 0.
the position marking where the string matched the delimiter. This is
the matched position and is initialized to 0.
the position marking the offset immediately following where the
string matched the delimiter
String#split operates in a loop. It continues to match the string against the delimiter until there are no more matches that can be found. It performs the following steps on each iteration:
from the start position match the delimiter against the string
set the matchedposition to where the delimiter matched the string
if the delimiter didn’t match the string then break the loop
create a substring using the start and matched positions of the
string being matched. Push this substring onto the results array
set the start position for the next iteration
With this knowledge let’s discuss how String#split handles the previous example of:
"1234".split(/1/) # => ["", "234"]
the first loop
the start position is initialized to 0
the delimiter is matched against the string “1234”
the first match occurs with the first character, “1” which is at
position 0. This sets the matched position to 0.
a substring is created using the start and matched positions and
pushed onto our result array. This gives us string[start,end] which
translates to “1234”[0,0] which returns an empty string.
the start position is reset to position 1
The second loop
start is now 1
The delimiter is matched against the remainder of our string, “234”
No match is found so the loop is finished.
A substring is created using the start position and remainder of the
string and pushed onto the results array
the results array is returned
Given how String#split works it is easy to see why we have that unexpected empty string in our results array. You should note that this only occurred because the regular expression matched our string at the first character. Below is an example where the delimiter doesn’t match the first character and there is no empty string:
"1234".split(/2/) # => ["1", "34"]
The pickaxe book says of string#split
If the limit parameter is omitted, trailing empty fields are suppressed. ... If negative, there is no limit to the number of fields returned and trailing null [empty] fields are not suppressed. So:
irb(main):001:0> "aware".split('e')
=> ["awar"]
irb(main):002:0> "aware".split('e',-1)
=> ["awar", ""]
I have a Ruby string variable with the value 1.14.2.ab3-4.dl0.rhel
However, I want to discard everything after the second decimal so that I get the value as 1.14
I am using the following command:
str.split(".")[0] but it doesn't seem to work
When you split by . on your string you get:
['1', '14', '2', 'ab3-4', 'dl0', 'rhel']
From this you can get the first two items joined by period:
str.split(".")[0..1].join(".")
# or
str.split(".").first(2).join(".")
With a regexp, you could just look for the first number with 2 decimals :
"1.14.2.ab3-4.dl0.rhel"[/\d+\.\d{2}/]
#=> "1.14"
#maxple's answer only works when the substring of interest is at the beginning of the string. As that was not part of the specification (only in the example), I don't think that's a reasonable assumption. (#Eric did not make that assumption.)
There is also ambiguity about your statement, "discard everything after the second decimal". #maxple interpreted that as after the second decimal point (but also discarded the second decimal point), whereas #Eric assumed it meant after the second decimal digit. This is what happens when questions are imprecise.
If the substring is at the beginning of the string, and you mean to discard the second decimal point and everything after, here are two ways to do that.
str = "1.14.2.ab3-4.dl0.rhel"
1. Modify #Eric's regex:
str[/\A\d+\.\d+/]
#=> "1.14"
2. Convert the string to a float and then back to a string:
str.to_f.to_s
#=> "1.14"
#1 returns nil if the desired substring does not exist, whereas #2 returns "0.0". As long as "0.0" is not a valid substring, either can be used to determine if the substring exists, and if it does, return the substring.
You could also use the partition method in String: https://ruby-doc.org/core-2.2.0/String.html#method-i-partition
"1.14.2.ab3-4.dl0.rhel".partition(/\d+\.\d{2}/)[1]
=> "1.14"
I am using LUA to create a table within a table, and am running into an issue. I need to also populate the NIL values that appear, but can not seem to get it right.
String being manipulated:
PatID = '07-26-27~L73F11341687Per^^^SCI^SP~N7N558300000Acc^'
for word in PatID:gmatch("[^\~w]+") do table.insert(PatIDTable,word) end
local _, PatIDCount = string.gsub(PatID,"~","")
PatIDTableB = {}
for i=1, PatIDCount+1 do
PatIDTableB[i] = {}
end
for j=1, #PatIDTable do
for word in PatIDTable[j]:gmatch("[^\^]+") do
table.insert(PatIDTableB[j], word)
end
end
This currently produces this output:
table
[1]=table
[1]='07-26-27'
[2]=table
[1]='L73F11341687Per'
[2]='SCI'
[3]='SP'
[3]=table
[1]='N7N558300000Acc'
But I need it to produce:
table
[1]=table
[1]='07-26-27'
[2]=table
[1]='L73F11341687Per'
[2]=''
[3]=''
[4]='SCI'
[5]='SP'
[3]=table
[1]='N7N558300000Acc'
[2]=''
EDIT:
I think I may have done a bad job explaining what it is I am looking for. It is not necessarily that I want the karats to be considered "NIL" or "empty", but rather, that they signify that a new string is to be started.
They are, I guess for lack of a better explanation, position identifiers.
So, for example:
L73F11341687Per^^^SCI^SP
actually translates to:
1. L73F11341687Per
2.
3.
4. SCI
5. SP
If I were to have
L73F11341687Per^12ABC^^SCI^SP
Then the positions are:
1. L73F11341687Per
2. 12ABC
3.
4. SCI
5. SP
And in turn, the table would be:
table
[1]=table
[1]='07-26-27'
[2]=table
[1]='L73F11341687Per'
[2]='12ABC'
[3]=''
[4]='SCI'
[5]='SP'
[3]=table
[1]='N7N558300000Acc'
[2]=''
Hopefully this sheds a little more light on what I'm trying to do.
Now that we've cleared up what the question is about, here's the issue.
Your gmatch pattern will return all of the matching substrings in the given string. However, your gmatch pattern uses "+". That means "one or more", which therefore cannot match an empty string. If it encounters a ^ character, it just skips it.
But, if you just tried :gmatch("[^\^]*"), which allows empty matches, the problem is that it would effectively turn every ^ character into an empty match. Which is not what you want.
What you want is to eat the ^ at the end of a substring. But, if you try :gmatch("([^\^])\^"), you'll find that it won't return the last string. That's because the last string doesn't end with ^, so it isn't a valid match.
The closest you can get with gmatch is this pattern: "([^\^]*)\^?". This has the downside of putting an empty string at the end. However, you can just remove that easily enough, since one will always be placed there.
local s0 = '07-26-27~L73F11341687Per^^^SCI^SP~N7N558300000Acc^'
local tt = {}
for s1 in (s0..'~'):gmatch'(.-)~' do
local t = {}
for s2 in (s1..'^'):gmatch'(.-)^' do
table.insert(t, s2)
end
table.insert(tt, t)
end