1
votes

I'm pretty new to Lua, so I have some troubles reading data from a csv file to a table. The csv file consist of four columns. The first column is a string the other three are double values. What I want to do is: Open the file, read in the data and process the data. For testing I want to print the data to the screen. Later I have to open a other file, a robot programm, and pass the data to this programm.

I execute the script with the consol command lua Script.lua. But all I get is the error message lua: Script.lua:22: bad argument #1 to ´format´ (number expected, got nil) stack traceback: [C]: in function ´string.format´ script.lua:22: in main chunk [C]: in?

Can somebody tell me what I'm doing wrong?

Edit: So I changed my Scritp a little bit. So this is my new code

local open = io.open

local function read_file(path)
    local file = open(path, "r") -- r read mode and b binary mode
    --if not file then return nil end
    local coordinates = {}

    for line in io.lines(path) do
    local coordinate_name, coordinate_x, coordinate_y, coordinate_z = line:match("%s*(.-),%s*(.-),%s*(.-),%s*(.-)")
    coordinates[#coordinates+1] = { coordinate_name=coordinate_name, coordinate_x = tonumber(coordinate_x), coordinate_y = tonumber(coordinate_y), coordinate_z = tonumber(coordinate_z) }
    end

    --file:close()
    return coordinates
end

local coordinates = read_file("data.csv")

for _, coordinate in ipairs(coordinates) do  -- use pairs or ipairs to iterate over tables
print(("X: %s, Y: %s, Z: %s"):format(coordinate.coordinate_x,
                                     coordinate.coordinate_y,
                                     coordinate.coordinate_z))
end

return 0;

Now I can execute the script, but everything printed to the screen is: X: nil, Y: nil, Z: nil. As far as I understand LUA, nil means that no values are read to the table.

Edit: The file I want to read looks like this:

After;-5;-5;0;
After;-2;-5;0;
After;5;-5;0;
After;5;-2;0;
After;5;5;0;
After;2;5;0;
After;-5;5;0;
After;-5;2;0;
After;-5;-5;0;
Intersects;5;-4;0
Intersects;-5;-4;0
Intersects;-5;-3;0
Intersects;5;-3;0
Intersects;5;-2;0
Intersects;-5;-2;0

Edit: The now updated code:

local open = io.open

local function read_file(path)
    local file = open(path, "r") -- r read mode and b binary mode
    --if not file then return nil end
    local coordinates = {}

    for line in io.lines(path) do
     local coordinate_name,
           coordinate_x,
           coordinate_y,
           coordinate_z = line:match("%s* (.*);%s*(.*);%s*(.*);%s*(.*);%s*(.*)")
     coordinates[#coordinates+1] = { coordinate_name = coordinate_name, coordinate_x = tonumber(coordinate_x), coordinate_y = tonumber(coordinate_y), coordinate_z = tonumber(coordinate_z) }
    print(("X: %s Y: %4f Z: %s"):format(coordinates.coordinate_x,
                                     coordinates.coordinate_y,
                                     coordinates.coordinate_z))
    end

    for _, coordinate in ipairs(coordinates) do
        print(coordinates.coordinate_x, coordinates.coordinate_z, coordinates.coordinate_z)
    end

    file:close()

    return coordinates
end

local coordinates = read_file("data.csv")
    for _, coordinates in ipairs(coordinates) do  -- use pairs or ipairs to iterate over tables
        print(("X: %s, Y: %s, Z: %s"):format(coordinates.coordinate_x,
                                         coordinates.coordinate_y,
                                         coordinates.coordinate_z))
    end

 return 0;

I'm using Lua 5.3.3 for Windows, the script is writen in LuaEdit and called by the line lua Script.lua.

1

1 Answers

4
votes

Use (.*) in your pattern instead of (.-). Per the docs, - will match the shortest pattern, which seems to bug out for the z coordinate in my testing:

Updated to match the file format posted

    local coordinate_name,
          coordinate_x,
          coordinate_y,
          coordinate_z = line:match("([^;]*);([^;]*);([^;]*);([^;]*)")

The tonumber will handle the space trimming for you and you don't seem to be using coordinate_name anywhere. There are plenty of string trim implementations you can choose from if you need to trim the coordinate_name variable later on.

Full script for reference.

local open = io.open

local function read_file(path)
    local file = open(path, "r") -- r read mode and b binary mode
    --if not file then return nil end
    local coordinates = {}

    for line in io.lines(path) do
        local coordinate_name,
        coordinate_x,
        coordinate_y,
        coordinate_z = line:match("([^;]*);([^;]*);([^;]*);([^;]*)")
        coordinates[#coordinates+1] = { coordinate_name = coordinate_name, coordinate_x = tonumber(coordinate_x), coordinate_y = tonumber(coordinate_y), coordinate_z = tonumber(coordinate_z) }
    end

    file:close()

    return coordinates
end

local coordinates = read_file("data.csv")
for _, coordinate in ipairs(coordinates) do  -- use pairs or ipairs to iterate over tables
    print(("X: %s, Y: %s, Z: %s"):format(coordinate.coordinate_x,
    coordinate.coordinate_y,
    coordinate.coordinate_z))
end

return 0;