4

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".

Yu Hao
  • 119,891
  • 44
  • 235
  • 294
Pier Paolo
  • 878
  • 1
  • 14
  • 23
  • 1
    tonumber parses floats itself: `for word in string.gmatch("0.0 1.295e-03", "[^%s]+") do print(tonumber(word)) end` – user2053898 Apr 05 '15 at 17:33

3 Answers3

3

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
Yu Hao
  • 119,891
  • 44
  • 235
  • 294
  • Thanks for your answer. Usually, however, the first line in the data file is made of words that describe the columns of numbers. Could I start reading from the second line in order to still have the array indexed from 1 and not from 2 (because if `split_number` reads words it returns `nil`)? – Pier Paolo Apr 05 '15 at 17:58
  • 1
    @PierPaolo Seems OK. There are more than one ways to skip the first line, choose an elegant way according to your specific problem. – Yu Hao Apr 05 '15 at 18:05
3

Lua can read numbers directly:

f=assert(io.open("data.txt"))
while true do
    local a,b=f:read("*n","*n")
    if b==nil then break end
    print(a,b)
end
f:close()
lhf
  • 70,581
  • 9
  • 108
  • 149
1

This works for on the lua REPL.

a = tonumber('4534.432')
b = tonumber('4534.432')
a==b 

So your answer is simply to use tonumber.

Software Mechanic
  • 974
  • 2
  • 9
  • 25