Riyas Muhammad
Riyas Muhammad

Reputation: 11

Literal, variable string replacement in Lua

I wrote the following code:

rpid = "Remote-Party-ID:<sip:+19>;abc"
local startRPID = string.find(rpid, "<")
local endRPID = string.find(rpid, ">")
local ANI = string.sub(rpid, startRPID, endRPID) 
local MRPID = "\"TEST\" "..ANI
local finalRPID = string.gsub(rpid,ANI,MRPID)
print (finalRPID)

Expected result:

Remote-Party-ID:"TEST" <sip:+19>;abc

However, the last replacement (string.gsub(rpid,ANI,MRPID)) is not happening because of the + character in ANI, so the actual result is Remote-Party-ID:<sip:+19>;abc.

If I pass the string without the + (rpid = "Remote-Party-ID:<sip:19>;abc"), the result is as expected (except for the +, which is of course also missing in the output).

How can I achieve the same with the + character included?

Upvotes: 1

Views: 156

Answers (2)

dt192
dt192

Reputation: 1013

This uses a balanced pair

local rpid = "Remote-Party-ID:<sip:+19>;abc"
rpid = rpid:gsub("%b<>", '"TEST" %1')
print(rpid)

Result:
Remote-Party-ID:"TEST" <sip:+19>;abc

Upvotes: 4

Luatic
Luatic

Reputation: 11171

The proper way to do it

Your code goes around a couple corners to do a simple thing: You want to find the text enclosed in angle brackets (< and >) and prepend something to it.

This can be done using a single call to string.gsub:

local RPID = "Remote-Party-ID:<sip:+19>;abc"
local finalRPID = rpid:gsub("<.->", function(sip)
    return '"TEST" ' .. sip
end)

This varies in some nuances:

  • It will be a no-op if there is no text enclosed by angle brackets.
  • It will perform multiple replacements if multiple angle-bracketed strings appear. You could add a limit of 1 as a third parameter to gsub to change this.

but it handles your scenario correctly. Alternatively, you could also use match; after all you don't need more than a single substitution:

local prefix, sip, suffix = RPID:match"(.-)(<.->)(.*)"
local finalRPID = prefix .. '"TEST" ' .. sip .. suffix

Fixing your code

You're right, the + character, serving as a quantifier for "one or more" in patterns, causes the issue since ANI is interpreted as a pattern. You can fix this by first escaping all magic characters in ANI using % signs: ANI:gsub("[][^$()%.*+-?]", "%%%1"). Similarly, you would need to escape % signs in MRPID (which I renamed to newANI) to avoid them being treated as "capture indices": MRPID:gsub("%%", "%%%%"). This yields:

local RPID = "Remote-Party-ID:<sip:+19>;abc"

// could just use `string.match` to extract this
local startANI, endANI = RPID:find"<", RPID:find">"
local ANI = RPID:sub(startANI, endANI)
-- Escape ANI to get a pattern for literal replacement.
local patt = ANI:gsub("[]%-%%[^$().*+?]", "%%%1")

local newANI = '"TEST" ' .. ANI
-- Escape to get a literal replacement string. Won't be needed here if there won't ever be `%` signs in `newANI`.
local repl = newANI:gsub("%%", "%%%%")

local finalRPID = RPID:gsub(ANI, newANI)

This code is however needlessly complicated; I merely fixed it to demonstrate how to do a literal string replacement in Lua by escaping the arguments of gsub. Extracted as a function:

function string:replace(needle, repl)
    needle = needle:gsub("[]%-%%[^$().*+?]", "%%%1")
    repl = repl:gsub("%%", "%%%%")
    return self:gsub(needle, repl)
end

(you might want to make this a local function to not modify the string table, which could conflict with other scripts doing the same)

Upvotes: 2

Related Questions