Search code examples
lua

Lua string gsub, not to use special %?


All I want is just plain string replace, but it seems that in Lua, I have to deal with regular expression. I have managed to write the following code, but even the '%' in non-literal string variable seems to be treated as a special character and causes an error where it is pointed by <--here below.

How can I print ==hello%20world== by the following code?

local path = "hello world"
path = path:gsub("( )", "%%20")

local test = "==$path=="
test = test:gsub("$path", path) <-- here

print(test)

Solution

  • Yes, string.gsub expects a pattern, but lua patterns are pretty simple, especially if you follow this rule: every special (non-alphanumeric) character can be literal if you preceed it by a %. Since you are using the percent sign also in the replacement string, things get a bit complicated. The solution for your problem is:

    local path = string.gsub("hello world", "%s", "%%%%20") -- note the quadruple %
    local test = string.gsub("==$path==", "(%$path)", path)
    print(test)     
    

    I used the %s character class to match whitespace in the first gsub. For the second gsub I used % to escape the dollar sign and I put a capture around the match, which is good practice in my opinion because then you have fine control over what %1, %2 etc. yield. But that is optional and only really required if you have more complex patterns.

    To me it seems like you are doing this in an over-complicated way. What are you actually trying to achieve? In this case, I would solve this with string.format:

    local test = string.format("==%s==", string.gsub("hello world", "%s", "%%20"))
    

    You still need the double %, but it looks simpler to me.

    But I feel that replacing spaces is weird, so maybe a string is not the right data structure here? You call that variable a path, so let's say the individual parts of that path are stored in a table. Then everything becomes:

    local elements = {    
        "hello", "world"    
    }    
    local test = string.format("==%s==", table.concat(elements, "%20"))    
    print(test)
    

    This is pretty straight-forward, I think (no extra escaping needed, no string handling, which is a benefit, IMHO).