Question

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?

Was it helpful?

Solution

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

OTHER TIPS

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

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top