I am new to the Lua library, I have one use case which I have to remove on a specific parameter and its value:
for example:
String 1 : ?xyz=true&toekn=4234dadsasda
String 2 : ?toekn=4234dadsasda&test=pass
Need output like this after removing token and its value
String 1 : ?xyz=true
String 2 : ?test=pass
I have tried the below Lua gsub function but no luck:
string.gsub(args, "token=.*", " ")
any help apricated, thanks
You may want to considers additional conditions (using & and ; as separators[1]) and corner cases (trailing separators and substrings with token):
text:gsub("([&;]?)%f[%a]token=[^&;]+([&;]?)",
function(s1, s2) return s1 and s2 and #(s1..s2) > 1 and s1 or "" end)
This solution works correctly on query strings that include parameters like subtoken and that use ; as separators. The template is using %f[%a], which is a frontier pattern that describes a zero-length boundary where non-letter changes to a letter (this includes the first character in a string).
[1] W3C recommends that all web servers support semicolon separators in addition to ampersand separators to allow application/x-www-form-urlencoded query strings in URLs within HTML documents without having to entity escape ampersands (wikipedia article on query string).
If you can only have two query params and no more than two as shown in your input you can use
text:gsub("&?token=[^&]+&?", "")
Or, if you have multiple query params, you can use
text:gsub("([&?])token=[^&]+&?", "%1"):gsub("(.*)&$", "%1")
See the online Lua demo #1 and the online Lua demo #2.
Details:
&? - an optional &
token= - a literal string
[^&]+ - one or more chars other than &
&? - an optional & char.
In the second solution, :gsub("([&?])token=[^&]+&?", "%1") replaces the match with either ? or & before the token, and the next gsub("(.*)&$", "%1") removes the & at the end of string in case the param occurs at the end of string.
Related
This question already has an answer here:
Parse parameters and values of smarty-like string in PHP
(1 answer)
Closed 2 years ago.
Question, basically
If I have a regex ((key1)(value1)|(key2)(value2)), key1 is ref'd by $2 & key2 by $4. Is it possible to combine these into the same reference? (I'm guessing no)
Thus $7 might be key & $8 might be value, regardless of which capture group it originated in
Any regex masters who can solve the below? I've spent a couple hours on it and am kinda stuck.
I would like it to work across different regex engines with minimal modifications. Been testing with PCRE on regexr.com
What I'm doing
I'm trying to make a file format that is parsed into key/value pairs with a single regex.
There's just a few rules:
Keys are a string of characters at the start of a line, followed by a colon (:).
So far, I'm just using [a-z]+ for the keys, but that will be expanded to some more characters. I don't think that will functionally change the regex.
values can be multi-line
all white-space is trimmed from values
I don't think I've added this to the regex yet
Values end when another key begins
delimiters can be used to wrap values in the format key:DELIM: then the value, then :DELIM: on it's own line.
Delimiter can be an empty string, thus :: serves as a delimiter
The regex I have
Correctly matches non-delimited keys & values
([a-z]+):((?:(?:.|\n|\r)(?!^[a-z]+:))+)
Correctly matches delimited keys & values
([a-z]+):([A-Z]*:)((.|\r|\n)*)^:\2
Matches everything correctly, BUT requires two sets of references
(?:(?:([a-z]+):([A-Z]*:)((.|\r|\n)*)^:\2)|([a-z]+):((?:(?:.|\n|\r)(?!^[a-z]+:))+))
$1 & $5 are keys. $3 & $6 are values
Sample Input
key: value 1
nightmare:DELIM:
notakey:
obviously not a key
notakey:
:DELIM:
abc: value 2
new line
anotherkey:: value
nostring: on this one
::
Which would yield These key/value pairs
key
value1
nightmare
notakey:
obviously not a key
notakey:
abc
value 2
new line
anotherkey
value
nostring: on this one
My latest attempt
My latest attempt got me here, but it doesn't actually match anything:
^([a-z]+): # key CP#1
((?:[A-Z]*:)? # delimiter, optional
(?:\s*(\r?\n|$)) # whitespace, new line OR end of file (line?)
) # CP#2
( # value, CP#3
(?:(?:
(?:.|\n|\r) # characters we want
(?!^[a-z]+:) # But NOT if those characters make up a key
)+)
| # or
((.|\r|\n)*) # characters we want
^:\2 # Ends with delimiter
) # delimited value
Thanks to the commenter for the ?| operator, which turns out to be what I needed.
((key1)(value1)|(key2)(value2)) => (?|(key1)(value1)|(key2)(value2)).
(?|(?:([a-z]+):([A-Z]*:)((.|\r|\n)*)^:\2)|([a-z]+):()((?:(?:.|\n|\r)(?!^[a-z]+:))+)) basically does it, though the final product certainly still needs more work.
I have a string, and I want to extract, using regular expressions, groups of characters that are between the character : and the other character /.
typically, here is a string example I'm getting:
'abcd:45.72643,4.91203/Rou:hereanotherdata/defgh'
and so, I want to retrieved, 45.72643,4.91203 and also hereanotherdata
As they are both between characters : and /.
I tried with this syntax in a easier string where there is only 1 time the pattern,
[tt]=regexp(str,':(\w.*)/','match')
tt = ':45.72643,4.91203/'
but it works only if the pattern happens once. If I use it in string containing multiples times the pattern, I get all the string between the first : and the last /.
How can I mention that the pattern will occur multiple time, and how can I retrieve it?
Use lookaround and a lazy quantifier:
regexp(str, '(?<=:).+?(?=/)', 'match')
Example (Matlab R2016b):
>> str = 'abcd:45.72643,4.91203/Rou:hereanotherdata/defgh';
>> result = regexp(str, '(?<=:).+?(?=/)', 'match')
result =
1×2 cell array
'45.72643,4.91203' 'hereanotherdata'
In most languages this is hard to do with a single regexp. Ultimately you'll only ever get back the one string, and you want to get back multiple strings.
I've never used Matlab, so it may be possible in that language, but based on other languages, this is how I'd approach it...
I can't give you the exact code, but a search indicates that in Matlab there is a function called strsplit, example...
C = strsplit(data,':')
That should will break your original string up into an array of strings, using the ":" as the break point. You can then ignore the first array index (as it contains text before a ":"), loop the rest of the array and regexp to extract everything that comes before a "/".
So for instance...
'abcd:45.72643,4.91203/Rou:hereanotherdata/defgh'
Breaks down into an array with parts...
1 - 'abcd'
2 - '45.72643,4.91203/Rou'
3 - 'hereanotherdata/defgh'
Then Ignore 1, and extract everything before the "/" in 2 and 3.
As John Mawer and Adriaan mentioned, strsplit is a good place to start with. You can use it for both ':' and '/', but then you will not be able to determine where each of them started. If you do it with strsplit twice, you can know where the ':' starts :
A='abcd:45.72643,4.91203/Rou:hereanotherdata/defgh';
B=cellfun(#(x) strsplit(x,'/'),strsplit(A,':'),'uniformoutput',0);
Now B has cells that start with ':', and has two cells in each cell that contain '/' also. You can extract it with checking where B has more than one cell, and take the first of each of them:
C=cellfun(#(x) x{1},B(cellfun('length',B)>1),'uniformoutput',0)
C =
1×2 cell array
'45.72643,4.91203' 'hereanotherdata'
Starting in 16b you can use extractBetween:
>> str = 'abcd:45.72643,4.91203/Rou:hereanotherdata/defgh';
>> result = extractBetween(str,':','/')
result =
2×1 cell array
{'45.72643,4.91203'}
{'hereanotherdata' }
If all your text elements have the same number of delimiters this can be vectorized too.
Okay so I wanted a regex to parse uncontracted(if that's what it is called) ipv6 adresses
Example ipv6 adress: 1050:::600:5:1000::
What I want returned: 1050:0000:0000:600:5:1000:0000:0000
My try at this:
ip:gsub("%:([^0-9a-zA-Z])", ":0000")
The first problem with this: It replaces the first and second :
So :: gets replaced with :0000
Replacing it with :0000: wouldn't work because then it will end with a :. Also this would note parse the newly added : resulting in: 1050:0000::600:5:1000:0000:
So what would I need this regex to do?
Replace every : by :0000 if it isn't followed by a number or letter
Main problem: :: gets replaced instead of 1 :
gsub and other functions from Lua's string library use Lua Patterns which are much simpler than regex. Using the pattern more than once will handle the cases where the pattern overlaps the replacement text. The pattern only needs to be applied twice since the first time will catch even pairings and the second will catch the odd/new pairings of colons. The trailing and leading colons can be handled separately with their own patterns.
ip = "1050:::600:5:1000::"
ip = ip:gsub("^:", "0000:"):gsub(":$", ":0000")
ip = ip:gsub("::", ":0000:"):gsub("::", ":0000:")
print(ip) -- 1050:0000:0000:600:5:1000:0000:0000
There is no single statement pattern to do this but you can use a function to do this for any possible input:
function fill_ip(s)
local ans = {}
for s in (s..':'):gmatch('(%x*):') do
if s == '' then s = '0000' end
ans[ #ans+1 ] = s
end
return table.concat(ans,':')
end
--examples:
print(fill_ip('1050:::600:5:1000::'))
print(fill_ip(':1050:::600:5:1000:'))
print(fill_ip('1050::::600:5:1000:1'))
print(fill_ip(':::::::'))
I'm trying to retrieve a filename without the extension in ColdFusion. I am using the following function:
REMatchNoCase( "(.+?)(\.[^.]*$|$)" , "Doe, John 8.15.2012.docx" );
I would like this to return an array like: ["Doe, John 8.15.2012","docx"]
but instead I always get an array with one element - the entire filename:["Doe, John 8.15.2012.docx"]
I tried the regex string above on rexv.org and it works as expected, but not on ColdFusion. I got the string from this SO question: Regex: Get Filename Without Extension in One Shot?
Does ColdFusion use a different syntax? Or am I doing something wrong?
Thanks.
Why you're not getting expected results...
The reason you are getting a one-item array with the whole filename is because your pattern matches the entire filename, and matches once.
It is capturing the two groups, but rematch returns arrays of matches, not arrays of the captured groups, so you don't see those groups.
How to solve the problem...
If you are dealing with simple files (i.e. no .htaccess or similar), then the simplest solution is to just use...
ListLast( filename , '.' )
....to get only the file extension and to get the name without extension you can do...
rematch( '.+(?=\.[^.]+$)' , filename )
This uses a lookahead to ensure there is a . followed by at least one non-. at the end of the string, but (since it's a lookahead) it is excluded from the match (so you only get the pre-extension part in your match).
To deal with non-extensioned files (e.g. .htaccess or README) you can modify the above regex to .+(?=(?:\.[^.]+)?$) which basically does the same thing except making the extension optional. However, there isn't a trivial way to get update the ListLast method for these (guess you'd need to check len(extension) LT len(filename)-1 or similar).
(optional) Accessing captured groups...
If you want to get at the actual captured groups, the closest native way to do this in CF is using the refind function, with the fourth argument set to true - however, this only gives you positions and lengths - requiring that you use mid to extract them yourself.
For this reason (amongst many others), I've created an improved regex implementation for CF, called cfRegex, which lets you return the group text directly (i.e. no messing around with mid).
If you wanted to use cfRegex, you can do so with your original pattern like so:
RegexMatch( '(.+?)(\.[^.]*$|$)' , filename , 1 , 0 , 'groups' )
Or with named arguments:
RegexMatch( pattern='(.+?)(\.[^.]*$|$)' , text=filename , returntype='groups' )
And you get returned an array of matches, within each element being an array of the captured groups for that match.
If you're doing lots of regex work dealing with captured groups, cfRegex is definitely better than doing it with CF's re methods.
If all you care about is getting the extension and/or the filename with extension excluded then the previous examples above are sufficient.
#Peter's response is great, however the approach is perhaps a bit longer-winded than necessary. One can do this with reMatch() with a slight tweak to the regex.
<cfscript>
param name="URL.filename";
sRegex = "^.+?(?=(?:\.[^.]+?)?$)";
aMatch = reMatch(sRegex, URL.filename);
writeDump(aMatch);
</cfscript>
This works on the following filename patterns:
foo.bar
foo
.htaccess
John 8.15.2012.docx
Explanation of the regex:
^ From the beginning of the string
.+? One or more (+) characters (.), but the fewest (?) that will work with the rest of the regex. This is the file name.
(?=) Look ahead. Make sure the stuff in here appears in the string, but don't actually match it. This is the key bit to NOT return any file extension that might be present.
(?: Group this stuff together, but don't remember it for a back reference.
. A dot. This is the separator between file name and file extension.
[^.]+? One or more (+) single ([]) non-dot characters (^.), again matching the fewest possible (?) that will allow the regex as a whole to work.
? (This is the one after the (?:) group). Zero or one of those groups: ie: zero or one file extensions.
$ To the end of the string
I've only tested with those four file name patterns, but it seems to work OK. Other people might be able to finetune it.
A few more ways of achieving the same result. They all execute in roughly the same amount of time.
<cfscript>
str = 'Doe, John 8.15.2012.docx';
// sans regex
arr1 = [
reverse( listRest( reverse( str ), '.' ) ),
listLast( str, '.' )
];
// using Java String lastIndexOf()
arr2 = [
str.substring( 0, str.lastIndexOf( '.' ) ),
str.substring( str.lastIndexOf( '.' ) + 1 )
];
// using listToArray with non-filename safe character replace
arr3 = listToArray( str.replaceAll( '\.([^\.]+)$', '|$1' ), '|' );
</cfscript>
I have a piece of lua code (executing in Corona):
local loginstr = "emailAddress={email} password={password}"
print(loginstr:gsub( "{email}", "tester#test.com" ))
This code generates the error:
invalid capture index
While I now know it is because of the curly braces not being specified appropriately in the gsub pattern, I don't know how to fix it.
How should I form the gsub pattern so that I can replace the placeholder string with the email address value?
I've looked around on all the lua-oriented sites I can find but most of the documentation seems to revolve around unassociated situations.
As I've suggested in the comments above, when the e-mail is encoded as a URL parameter, the %40 used to encode the '#' character will be used as a capture index. Since the search pattern doesn't have any captures (let alone 40 of them), this will cause a problem.
There are two possible solutions: you can either decode the encoded string, or encode your replacement string to escape the '%' character in it. Depending on what you are going to do with the end result, you may need to do both.
the following routine (I picked up from here - not tested) can decode an encoded string:
function url_decode(str)
str = string.gsub (str, "+", " ")
str = string.gsub (str, "%%(%x%x)",
function(h) return string.char(tonumber(h,16)) end)
str = string.gsub (str, "\r\n", "\n")
return str
end
For escaping the % character in string str, you can use:
str:gsub("%%", "%%%%")
The '%' character is escaped as '%%', and it needs to be ascaped on both the search pattern and the replace pattern (hence the amount of % characters in the replace).
Are you sure your problem isn't that you're trying to gsub on loginurl rather than loginstr?
Your code gives me this error (see http://ideone.com/wwiZk):
lua: prog.lua:2: attempt to index global 'loginurl' (a nil value)
and that sounds similar to what you're seeing. Just fixing it to use the right variable:
print(loginstr:gsub( "{email}", "tester#test.com" ))
says (see http://ideone.com/mMj0N):
emailAddress=tester#test.com password={password}
as desired.
I had this in value part so You need to escape value with: value:gsub("%%", "%%%%").
Example of replacing "some value" in json:
local resultJSON = json:gsub(, "\"SOME_VALUE\"", value:gsub("%%", "%%%%"))