3

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 ?

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
Paka
  • 1,053
  • 11
  • 20
  • It looks like sometimes there can be no `detail`s nor `address`es, what do you want to happen then? – CertainPerformance Aug 16 '18 at 05:45
  • @CertainPerformance I am trying to make regex in such a way that it matches all the condition (ie match all 3 values or match only 2 values or match only 1 value) and if matches detail or address then s2 and s3 will have value if not s2 and s3 will not have value – Paka Aug 16 '18 at 06:33

3 Answers3

3
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)
Egor Skriptunoff
  • 906
  • 1
  • 8
  • 23
2

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.

wp78de
  • 18,207
  • 7
  • 43
  • 71
  • 2
    While It is a clever solution, it cannot distinguish between the exact `/school/student/...` and any other string with slashes, AFAIK. Not important if OP just works with `/school/student/...`, although. – Julio Aug 16 '18 at 07:39
2

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.

Julio
  • 5,208
  • 1
  • 13
  • 42