Search code examples
stringlualua-patterns

How to match a floating point number when reading a string


How can I match floating point numbers like 1.234 or that use the "E notation" like 1.23e04 when dealing with strings?

As an example, let's say that I would like to read numbers from a data file like the following:

0.0 1.295e-03
0.1 1.276e-03
0.2 1.261e-03
0.3 1.247e-03
0.4 1.232e-03
0.5 1.218e-03

At the moment I wrote my own function to convert each line in the numbers it contains, but it's not very elegant and not portable at all: data files with a different "layout" will give errors.

Here is a simple example that reads the data file already presented and prints to screen the numbers:

function read_line(str)
   local a, b, c, d, e = str:match(
      "^%s*(%d+)%.(%d+)%s+(%d+)%.(%d+)[Ee]%-*(%d+)")
   if str:match("%-") then
      e = -tonumber(e)
   end
   local v1 = a + .1*b
   local v2 = (c + .001*d) * 10^e
   return v1, v2
end

for line in io.lines("data.txt") do
   print(read_line(line))
end

and this gives as a result:

0   0.001295
0.1 0.001276
0.2 0.001261
0.3 0.001247
0.4 0.001232
0.5 0.001218

This is indeed the result I want to achieve, but is there a more elegant and general way to deal with this problem?

Note: Data files can have more than two columns of numbers and can have both the floating point representation and the "E notation".


Solution

  • Assuming every line contains only whitespace separated numbers, you can let tonumber do the heavy work instead of matching numbers manually:

    function split_number(str)
        local t = {}
        for n in str:gmatch("%S+") do
            table.insert(t, tonumber(n))
        end
        return table.unpack(t)
    end
    
    for line in io.lines("data.txt") do
        print(split_number(line))
    end