1

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 Answer 1

4

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;
Sign up to request clarification or add additional context in comments.

15 Comments

I just tried your pattern to read the csv file. But still all printed to the console window is: X: nil, Y. nil, Z: nil. Is there a way to test if the read function works and test the print seperatly. Maybe the read function works, but my print function is wrong.
Your print function works fine for me. Perhaps there's something wrong with your file or maybe it's not reading it at all because it's not in the same folder. Try printing out the line variable in your read_file loop and see if any output shows up and to double-check that the contents are in the right format.
I copied the print line into the loop for line in io.lines(path) do and still get the nil as answer. I also checked that the file is in the same folder as the lua script and that the command line is opened in the same folder. I also edited my question so you see what the file looks like.
Ah, there's your problem. Your fields are separated by semicolons, not commas. Try line:match("%s*(.*);%s*(.*);%s*(.*);%s*(.*)")
I changed the line you suggested, but still nothing is printed to screen. But I still have a question. If I'm reading in the coordinates as a string and than convert these coordinates to numbers by the command tonumber, is it correct to print it with %s? Wouldn't %d be better, like in C?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.