Paka
Paka

Reputation: 1053

Regex pattern issue in lua

I have a URL string and need to get certain word from a match.

Example:

/school/student/studentname1/detail/55/address/address1

I am able to pass to fetch detail of the needed one like,

local s1,s2,s3 =myString:match("/school/student/(.-)/detail/(.-)/address/(.-)")

Now the problem is that my string can be

myString = /school/student/studentname1

or

myString = /school/student/studentname1/detail/55

In that case my regex is failing, any help ?

Upvotes: 3

Views: 350

Answers (3)

wp78de
wp78de

Reputation: 18950

This is the best one-liner I could come up with:

local s1,s2,s3 =myString:match("/[^/]+/[^/]+/([^/]+)/?[^/]*/?([^/]*)/?[^/]*/?([^/]*)")

Demo

Explanation:

I use negated character classes to get the text between slashes in a generic way. This makes it easier to flag the later parts as optional using * for classes and ? for slashes (you can make the initial part less generic and just use /school/student/).

This would be easy using (PC)RE, however, Lua Patterns do not support optional capture groups as well as alternations. Instead, you can use PCRE patterns in Lua with the rex_pcre library or use the pattern-matching library Lpeg.

Upvotes: 2

Julio
Julio

Reputation: 5308

To begin with, your original patter was not working as expected since it did not capture the address (because you used .- which is not greedy)

So one way to fix the original patter could be using /school/student/([^/]+)/detail/([^/]+)/address/([^/]+)

where [^/] means any character except /

Then, in order to optionally match some options and since lua patterns does not allow optional groups, you may need to use several steps like this:

myString = "/school/student/studentname1/detail/55"
local s1,s2,s3
s1 =myString:match("/school/student/([^/]+)")
if (s1 ~= nil) then
  print(s1)
  s2 =myString:match("/detail/([^/]+)")
  if (s2 ~= nil) then
    print(s2)
    s3 =myString:match("/address/([^/]+)")
    if (s3 ~= nil) then
      print(s3)
    end
  end
end

Finally, if you want to make sure that detail and address appear exactly on that order, you may use this:

myString = "/school/student/studentname1/address/myaddress"
local s1,s2,s3
s1 =myString:match("/school/student/([^/]+)")
if (s1 ~= nil) then
  print(s1)
  s1,s2 =myString:match("/school/student/([^/]+)/detail/([^/]+)")
  if (s2 ~= nil) then
    print(s2)
    s1,s2,s3 =myString:match("/school/student/([^/]+)/detail/([^/]+)/address/([^/]+)")
    if (s3 ~= nil) then
      print(s3)
    end
  end
end

That way it will find /school/student/studentname1/detail/55 but it will not find /school/student/studentname1/address/myaddress. If you don't need it like this, just use the first version.

Upvotes: 2

Egor   Skriptunoff
Egor Skriptunoff

Reputation: 974

local all_fields = { student = 1, detail = 2, address = 3 }

local function parse(str)
   local info = {}
   local index
   for w in str:gmatch"/([^/]+)" do
      if index then
         info[index] = w
         index = nil
      else
         index = all_fields[w]
      end
   end
   return (table.unpack or unpack)(info, 1, 3)
end

local myString = '/school/student/studentname1/detail/55/address/address1'
local s1, s2, s3 = parse(myString)
print(s1, s2, s3)

myString = '/school/student/studentname1/address/address1'
s1, s2, s3 = parse(myString)
print(s1, s2, s3)

myString = '/school/student/studentname1/detail/55'
s1, s2, s3 = parse(myString)
print(s1, s2, s3)

Upvotes: 3

Related Questions