Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to specify the argument positions in the format string for `string.format`?

Tags:

printf

lua

In C, I can tell printf to print the arguments in an order different than the order they are passed in:

printf("%2$d %1$d\n", 10, 20);
//prints 20 10

However, if I try to do the same in Lua I get an error:

print(string.format("%2$d %1$d\n", 10, 20))
invalid option '%$' to 'format'

Is there a way to create a Lua format string that causes string.format to write the second argument before the first? I am working with an internationalization and changing the format string is easy but changing the argument order is much more tricky.

I would have expected the technique that I used in C to work with Lua because, according to the manual, string.format should receive the same parameters as sprintf. Are %2$ directives not part of ANSI C or is the Lua manual just forgetting to mention that they are not supported?

like image 594
hugomg Avatar asked Dec 01 '13 23:12

hugomg


2 Answers

In short, no. %2$ directives are a POSIX extension, thus not part of ANSI C or Lua. This has been brought up on the Lua mailing list before, and according to lhf, the feature was around in versions prior to Lua 5 but was removed with that version's release. In the same thread, a wiki page of alternatives was suggested.

If you really want the %2$ style, then it's not too difficult to cook up your own fix either.


local function reorder(fmt, ...)
    local args, order = {...}, {}

    fmt = fmt:gsub('%%(%d+)%$', function(i)
        table.insert(order, args[tonumber(i)])
        return '%'
    end)

    return string.format(fmt, table.unpack(order))
end

print(reorder('%2$d %1$d\n', 10, 20))
like image 156
Ryan Stein Avatar answered Sep 27 '22 23:09

Ryan Stein


You cannot do this with string.format, but you can actually achieve almost the same result with string.gsub. The caveat here is that the last argument of string.gsub can be either string or table (with multiple values to replace)

So this code would do the trick:

local output = string.gsub("%2 %1 %2 %1\n", '%S+', {['%1'] = 10, ['%2'] = 20})
print(output)

> 20 10 20 10
like image 41
nogard Avatar answered Sep 28 '22 00:09

nogard