2
votes

I'm still getting used to the concept of string patterns, and I've run into an issue regarding them. I'm trying to create a simple program that searches a string of text, for certain characters encapsulated in whatever the brackets may be. Here's an example:

local str = "Hello <<world>>, my <<name>> is <<John>>"

-- Match patterns with << ... >>
for noun in str:gmatch("<<.->>") do
    print(noun)
end

This program will search through the string, matching everything that starts with << and ends with >>, and everything in between. Good, that's what I want. However, let's say I wanted a different pattern that only got text between one of those tags instead of two (< and > instead of << and >>). This is where I run into a problem:

-- Allow easy customization control over brackets
local matchNouns = {"<<", ">>"}
local matchOther = {"<", ">"}

local str = "<Hello> <<world>>, <my> <<name>> <is> <<John>>"

local function printOtherMatches(str)
    -- Get opening and closing brackets
    local open, close = unpack(matchOther)

    -- Concatenate opening and closing brackets with
    -- pattern for finding all characters in between them
    for other in str:gmatch(open .. ".-" .. close) do
        print(other)
    end
end

printOtherMatches(str)

The program above will print everything between < and > (the matchOther elements), however it also prints text captured with << and >> as well. I only want the iterator to return patterns that explicitly match the opening and closing tags. So the output from above should print:

<Hello>

<my>

<is>

Instead of:

<Hello>

<<world>>

<my>

<<name>>

<is>

<<John>>

Basically, just like with markdown how you can use * and ** for different formats, I'd like to create a string pattern for that in Lua. This was my attempt of emulating that kind of pattern sequence. If anyone has any ideas, or insight on how I could achieve this, I'd really appreciate it!

2
Not sure, but perhaps, a frontier pattern is what you need: local matchOther = {"%f[<]<%f[^<]", "%f[>]>%f[^>]"}, check ideone.com/Wf2QQS.Wiktor Stribiżew
Does it work as expected, or do you think that does not fit the scenario?Wiktor Stribiżew
I'm curious, do you need to account for all characters between the < and >, or can we be specific (only letters)? And do we know if there will always be spaces before and after each tag as given in your example? For example, I likely would solve your example by taking both into account and using the following: for other in str:gmatch('%s?<%a+>%s') do print(other) end That said, I'm not sure this will account for all scenarios you're looking for, can you provide more information?Greymanic
Yeah, that actually did. I didn't know about the %f magic character, but I'll do some experimenting with it.user7385467
See Egor's answer, it might be more comprehensive. If you prefer my solution, I will post as an answer.Wiktor Stribiżew

2 Answers

0
votes
-- Allow easy customization control over brackets
local matchNouns = {"<<", ">>"}
local matchOther = {"<", ">"}
local delimiter_symbols = "<>"  -- Gather all the symbols from all possible delimiters above

local function printMatches(str, match_open_close)
   -- Get opening and closing brackets
   local open, close = unpack(match_open_close)
   -- Concatenate opening and closing brackets with
   -- pattern for finding all characters in between them
   for other in str:gmatch(
      "%f["..delimiter_symbols:gsub("%p", "%%%0").."]"
      ..open:gsub("%p", "%%%0")
      .."%f[^"..delimiter_symbols:gsub("%p", "%%%0").."]"
      .."(.-)"
      .."%f["..delimiter_symbols:gsub("%p", "%%%0").."]"
      ..close:gsub("%p", "%%%0")
      .."%f[^"..delimiter_symbols:gsub("%p", "%%%0").."]"
   ) do
      print(other)
   end
end

local str = "<Hello> <<world>>, <my> <<name>> <is> <<John>>"
printMatches(str, matchOther)
0
votes

Here's one possibility:

local s = '<Hello> <<world>>, <my> <<name>> <is> <<John>>'

for s in s:gmatch '%b<>' do
  if not s:sub(2,-2):match '%b<>' then print(s) end
end