Difference between revisions of "Introduction to Lua"
(Created page with "=== Overview === Lua was created in 1993 by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, and Waldemar Celes, members of the Computer Graphics Technology Group at PUC-R...") |
|||
(25 intermediate revisions by 4 users not shown) | |||
Line 1: | Line 1: | ||
− | + | The Ultimate Guide to Gideros Studio | |
− | + | __TOC__ | |
− | Lua is used in commercial applications, including Adobe's Photoshop Lightroom, World of Warcraft, Far Cry, Garry's Mod, Supreme Commander and Sonic the Hedgehog. | + | == Overview == |
+ | Lua was created in 1993 by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, and Waldemar Celes, members of the Computer Graphics Technology Group at PUC-Rio, the Pontifical University of Rio de Janeiro. Lua is used in commercial applications, including Adobe's Photoshop Lightroom, World of Warcraft, Far Cry, Garry's Mod, Supreme Commander and Sonic the Hedgehog. The advantages of Lua are its simplicity and its speed. Lua is mentioned as the fastest language among all interpreted scripting languages. | ||
− | + | In Gideros Studio, you usually write your code in Lua however, you can also extend your applications with other languages like Objective-C, C++, C or Java. Gideros Studio, with Lua, produces fast and lightweight applications and games. | |
− | + | Despite the size of the Lua interpreter (about 150Kb in size), Lua is very rich and can be extended. | |
− | < | + | Let’s start with the simplest Lua code which is printing to the console: |
+ | <syntaxhighlight lang="lua"> | ||
print ("Hello, Gideros Studio") | print ("Hello, Gideros Studio") | ||
− | </ | + | </syntaxhighlight> |
− | Here print() is a Lua function. We invoke a function using | + | Here, print(...) is a Lua function. We invoke a function using parenthesis. Print() takes one argument which is the text between double quotes. The code writes "Hello, Gideros Studio" to the standard output (the console). |
− | We use "--" to start a comment, or use --[[ to start a | + | We use "--" to start a comment, or use --[[ to start a multi-line comment. Consider this example: |
+ | <syntaxhighlight lang="lua"> | ||
+ | -- this is a single line comment | ||
− | + | --[[ and this is a multi-line comment | |
− | --[[ | + | it goes on and on... |
− | + | and on... | |
− | |||
]] | ]] | ||
− | </ | + | </syntaxhighlight> |
− | + | == Assignments and variables == | |
− | + | Lua is a dynamically-typed language, that is, variables don't have types, only values. All values carry their own type. Consider the following as an example: | |
− | + | <syntaxhighlight lang="lua"> | |
− | |||
− | Lua is a dynamically-typed language, that is, variables don't have types, only | ||
− | |||
− | < | ||
MyTable = wide | MyTable = wide | ||
width = 3; height = 1.5 | width = 3; height = 1.5 | ||
area = width * height | area = width * height | ||
print (area) | print (area) | ||
− | </ | + | </syntaxhighlight> |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | + | You can nest several lines using semicolons, but it’s optional and generally not a good programming habit as it decreases the readability of your code. Be careful not to use reserved words. Gideros Studio will complain when you use a reserved word in Lua. Consider this example: | |
+ | <syntaxhighlight lang="lua"> | ||
+ | local = 1 -- this will not compile because local is reserved. | ||
+ | </syntaxhighlight> | ||
+ | Since Lua is a small language, it has only 8 basic data types: | ||
# nil (null) | # nil (null) | ||
# Booleans | # Booleans | ||
Line 54: | Line 51: | ||
# Tables | # Tables | ||
− | Global variables in Lua | + | Global variables in Lua don't need to be declared. If you want to declare a global variable, just assign it a value. |
+ | Giving a variable a nil value removes it. When a variable is removed and is not referenced by any other variables, it is cleared from memory thanks to Lua garbage collector. | ||
+ | |||
+ | Lua can support multiple assignments: | ||
+ | <syntaxhighlight lang="lua"> | ||
+ | a, b, c = 1, 2, 3 | ||
+ | -- Easy method to swap two variables | ||
+ | x, y = y, z | ||
+ | -- Function can return multiple values | ||
+ | n, m = calculate (phi, zeta) | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | == Control structures == | ||
+ | Lua provides a strong control mechanism between different variable types. As an example of logical decisions, consider the following: | ||
+ | |||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! Logical decision !! Meaning | ||
+ | |- | ||
+ | | A and B || If A is true, then return the result of expression B. | ||
+ | If A is false, then return A | ||
+ | |- | ||
+ | | A or B || If A is true, then return the result of expression A | ||
+ | If A is false, then return B | ||
+ | |} | ||
+ | |||
+ | == Arrays == | ||
+ | Arrays can be indexed not only with numbers but with any values. Arrays do not have to be defined a size, they grow as needed. When an array is defined, and you try to reach a value exceeding its boundary, you get a nil value. | ||
+ | |||
+ | Arrays can have any index, e.g. you can start an array with -5, 0 or 1 - it’s all up to you. Consider the following example: | ||
+ | <syntaxhighlight lang="lua"> | ||
+ | -- creates an array with indices from -2 to 2 | ||
+ | array = {} | ||
+ | for i=-2, 2 do | ||
+ | array[i] = "apples" | ||
+ | end | ||
+ | </syntaxhighlight> | ||
− | Lua | + | Lua also supports multi-dimensional arrays, which we call "matrices". |
+ | The statement below: | ||
+ | <syntaxhighlight lang="lua"> | ||
+ | animals = {"giraffe", "boar", "wolf", "frog"} | ||
+ | </syntaxhighlight> | ||
− | < | + | is equal to: |
− | + | <syntaxhighlight lang="lua"> | |
+ | animals = {} | ||
+ | animals[1] = "giraffe"; animals[2] = "boar" | ||
+ | animals[3] = "wolf"; animals[4] = "frog" | ||
+ | </syntaxhighlight> | ||
− | + | Similar array syntax can be used to create string lists. | |
− | |||
− | + | == Linked lists == | |
− | |||
− | |||
− | == | + | == Lua tables (arrays) == |
+ | In Lua, Tables and Arrays are terms used interchangeably, However, this is true to an extent. | ||
+ | == Basic functions == | ||
Lua has the ability to call its own functions or C functions, and it handles functions as a "data type". Note that functions can return multiple values, and you can use multiple assignments to collect these functions. | Lua has the ability to call its own functions or C functions, and it handles functions as a "data type". Note that functions can return multiple values, and you can use multiple assignments to collect these functions. | ||
− | + | <syntaxhighlight lang="lua"> | |
− | < | ||
function calculate(w, d, h) | function calculate(w, d, h) | ||
− | + | if h > 4 then | |
− | + | print ("The height of the room cannot be more than 4.") | |
− | + | return | |
− | + | end | |
− | + | volume = w * d * h | |
− | + | return volume | |
end | end | ||
− | + | -- calling the function to calculate the volume of the room | |
− | -- | + | print( calculate(4,3,4) ) |
− | print (calculate (4,3,4)) | + | </syntaxhighlight> |
− | </ | ||
Try the function above, this time with values 4,5,5. | Try the function above, this time with values 4,5,5. | ||
− | + | == String manipulation == | |
− | + | Lua has a powerful set of string manipulation functions, such as finding and extracting substrings. Unlike C, the first character of a string has an index of 1 by default. | |
− | Lua has a powerful set of string manipulation functions, such as finding and extracting substrings. Unlike C, the first character of | ||
− | Note that the string library assumes one-byte character | + | Note that the string library assumes one-byte character encoding. |
The following table gives an overview of string manipulation commands. Have a look at them, and then study the examples below. | The following table gives an overview of string manipulation commands. Have a look at them, and then study the examples below. | ||
Line 98: | Line 135: | ||
{| class="wikitable" | {| class="wikitable" | ||
|- | |- | ||
− | ! String | + | ! String command !! Meaning |
|- | |- | ||
| string.byte (s [, i [, j]]) || Returns the internal numerical codes of the characters s[i], s[i+1], ···, s[j]. The default value for i is 1; the default value for j is i. | | string.byte (s [, i [, j]]) || Returns the internal numerical codes of the characters s[i], s[i+1], ···, s[j]. The default value for i is 1; the default value for j is i. | ||
Line 106: | Line 143: | ||
| string.format (formatstring, ···) || Returns a formatted version of its variable number of arguments following the description given in its first argument (which must be a string). | | string.format (formatstring, ···) || Returns a formatted version of its variable number of arguments following the description given in its first argument (which must be a string). | ||
|- | |- | ||
− | | string.gmatch (s, pattern) || Returns an iterator function that, each time it is called, returns the next | + | | string.gmatch (s, pattern) || Returns an iterator function that, each time it is called, returns the next capture from pattern over string s. |
|- | |- | ||
| string.len (s) || Returns the length of the string s. | | string.len (s) || Returns the length of the string s. | ||
Line 114: | Line 151: | ||
| string.upper (s) || Changes all characters of a string to uppercase. | | string.upper (s) || Changes all characters of a string to uppercase. | ||
|- | |- | ||
− | | string.reverse (s) || Returns a string which is | + | | string.reverse (s) || Returns a string which is the reverse of the string s. |
+ | |- | ||
|} | |} | ||
+ | |||
+ | == Mathematical functions == | ||
+ | |||
+ | == File I/O == | ||
+ | In an app, there always comes a point where you might want to persist some data. Persisting data in simple terms means retaining the data even after the app is exited. Some examples would include the current level, a high score, the player‘s name and so on. There could be other more complex examples where one might have levels stored in files. For all of these, there’s an API, the File I/O API. | ||
+ | |||
+ | There are two ways to reference these functions, the explicit and the implicit. Explicit is when the file to be worked on is specified, it is seen in the form of file: type commands. Implicit is passing the fileHandle to the function for file operations and are generally seen starting with io. Example: | ||
+ | <syntaxhighlight lang="lua"> | ||
+ | local playerName = "Player1" | ||
+ | local file = io.open ( "settings.save" , "w+" ) | ||
+ | file:write( "Playername:" , playerName ) | ||
+ | file:close() | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | This has just saved a file called settings.save with the values "Playername: Player1". | ||
+ | |||
+ | Now that we have some data saved in the file, let us try to read those from the file. | ||
+ | <syntaxhighlight lang="lua"> | ||
+ | local playerName = nil | ||
+ | local file = io.open (“settings.save”, “r”) | ||
+ | playerName = file:read() | ||
+ | file:close() | ||
+ | print ( “The player name saved is: ”, playerName) | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | === Opening a file === | ||
+ | To open a file using Lua, we would use: | ||
+ | <syntaxhighlight lang="lua"> | ||
+ | file = io.open ( filename [, mode]) | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Where filename is the path to a file stored on your system. The variable file is passed a reference to the filehandle. The modes for opening the file can be one of the following: | ||
+ | * “r” read mode, this is the default mode | ||
+ | * “w” write mode | ||
+ | * “a” append mode | ||
+ | * “r+” update mode, all previous data is preserved | ||
+ | * “w+” update mode, all previous data is erased | ||
+ | * “a+” append update mode, previous data is preserved, writing is allowed only at the end of the file. | ||
+ | |||
+ | There is no explicit way to open a file, all files are opened using the io.open function. | ||
+ | |||
+ | === Closing a file === | ||
+ | To close a file, we would use: | ||
+ | <syntaxhighlight lang="lua"> | ||
+ | io.close([file]) | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | This closes the filehandle passed to the function. If nothing is passed, the default output file is closed. | ||
+ | The explicit way to close a file is file:close() | ||
+ | |||
+ | === Getting data / Input === | ||
+ | In a complex application, we need to get several kinds of data from files stored on the device. In order to get data from a file, we are given a couple of options, including lines and read. | ||
+ | |||
+ | When we use the lines function, it offers an iterator function, which returns a new line from the file each time it is called. Therefore this is used as: | ||
+ | <syntaxhighlight lang="lua"> | ||
+ | for line in io.lines(filename) do … end | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Note that this function does not require the file to be opened or closed, all of that is managed by the function internally. | ||
+ | |||
+ | There is an explicit version of the same, which is used as file:lines(), the difference between this and the implicit function is that to get the filehandle, we have to open the file, there is no parameter passed to the lines function and when the end of file is reached, the file is not automatically closed, we have to close it. | ||
+ | |||
+ | The other way to get data from a file is the read function, which is used as: | ||
+ | <syntaxhighlight lang="lua"> | ||
+ | data = io.read(format1, …) | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | The explicit equivalent is: | ||
+ | file:read( format1, … ) | ||
+ | |||
+ | Since the function does not have a filehandle passed to it, io.read reads from the standard input. | ||
+ | |||
+ | The formats that can be used for the function are: | ||
+ | * “*n” this will read n number of characters from the file. | ||
+ | * “*a” this will read all the data in the file, starting from the current position. | ||
+ | * “*l” this will read the next line, this is the default for this command. | ||
+ | |||
+ | === Temporary files === | ||
+ | Sometimes there might be the need to create a temporary file either to transfer portions of data or to save a backup copy. The easiest way to create one is to use the io.tmpfile() function. This returns a filehandle to a temporary file opened in update mode and this file is automatically removed when the app terminates. | ||
+ | |||
+ | === Writing data === | ||
+ | To write data to a file, it has to be opened in a mode that supports writing data. The only way to write data is to use the function write: | ||
+ | <syntaxhighlight lang="lua"> | ||
+ | io.write(value1, …) | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | The data passed to the function is written to the file, however since there is no filehandle passed to this function, it writes to the standard output, so if you want to write to a file, the file:write(value1, ...) function should be used. | ||
+ | |||
+ | === Seeking your position in the file === | ||
+ | Updating data is one of the easiest and one of the most difficult task. An easy way to update data is by positioning the write head and then write the data there. We can get the position and set the position by using the function seek, this is an explicit function only. | ||
+ | <syntaxhighlight lang="lua"> | ||
+ | position = file:seek( [mode] [,offset] ) | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | By default, the mode is set to “cur” and the offset to 0. Three modes are available: | ||
+ | * “set”: this is from the 0 position (start of file) and the head is moved to the position as specified by offset | ||
+ | * “cur”: this is from the current position, so the offset is added to the current position | ||
+ | * “end”: this is from the end of the file | ||
+ | |||
+ | To get the size of the file, one can use: | ||
+ | <syntaxhighlight lang="lua"> | ||
+ | filesize = file:seek(“end”) | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Set can be used to rewind the reading. | ||
+ | |||
+ | === Buffering === | ||
+ | The buffering for a file can be set by the file:setvbuf() command which is defined as: | ||
+ | <syntaxhighlight lang="lua"> | ||
+ | file:setvbuf( mode [, size] ) | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Three modes are available for this command: | ||
+ | * “no”: There is no buffering, the results are instantaneous | ||
+ | * “full”: The output operations occur only when the buffer is full, or when you the flush command | ||
+ | * “line”: The output is buffered on a per line basis (till a newline character is received) | ||
+ | |||
+ | The size is the size of the buffer in bytes. | ||
+ | |||
+ | === Flushing data === | ||
+ | If you are using a buffer for writing operations, you might want to flush i.e. commit the file’s output buffer and save all of that to the file. Buffering is not really required in modern systems but still can be helpful at times. So before closing a file, if buffering was on, it is a good practice to flush and ensure that all the data was saved from the buffers to the file. This is an explicit function and is defined as: | ||
+ | <syntaxhighlight lang="lua"> | ||
+ | file:flush() | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | There are no parameters passed to this function. | ||
+ | |||
+ | === Validating the file operations === | ||
+ | While working with files there are a lot of situations that could lead to errors, so it is very important to check for the return values. In general, the return value is nil when the end of the file was reached or a file could not be opened, etc. | ||
+ | |||
+ | To check the state of a file passed with a filehandle we use the type function as: | ||
+ | <syntaxhighlight lang="lua"> | ||
+ | result = io.type(fileHandle) | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | The returned value is either “file” if the file is open, “closed” if the file is closed or nil if the fileHandle was not found. | ||
+ | |||
+ | == Lua resources == | ||
+ | This guide is by no means complete, and we suggest you have a good book covering Lua. There are many resources on the net, and here are a few of them: | ||
+ | * '''Official Lua Reference Manuals''': https://www.lua.org/manual/ | ||
+ | * Programming in Lua, second edition by Roberto Ierusalimschy (http://www.inf.puc-rio.br/~roberto/pil2/) | ||
+ | * Learn Lua in 15 minutes (http://tylerneylon.com/a/learn-lua/) | ||
+ | * Litt's Lua Laboratory: The 10% you need for 90% of your work (http://www.troubleshooters.com/codecorn/lua/index.htm) | ||
+ | * Official Lua FAQ (http://www.lua.org/faq.html) | ||
+ | * Classic data structures and algorithms in Lua (https://github.com/EvandroLG/computer_science_in_lua) | ||
+ | * Very nice Lua reference manual helper (http://pgl.yoyo.org/luai/i/_) | ||
+ | * Lua cheatsheet (https://devhints.io/lua) | ||
+ | * Lua C API short reference (http://www.ewald-arnold.de/lua/lua-apiref.pdf) | ||
+ | * Lua: Good, bad, and ugly parts (http://notebook.kulchenko.com/programming/lua-good-different-bad-and-ugly-parts) | ||
+ | * Introduction to Lua programming (http://w3.linux-magazine.com/issue/45/Lua_Programming.pdf) | ||
+ | * Lua performance optimization tips (https://springrts.com/wiki/Lua_Performance) | ||
+ | * Lua video tutorial series by CODE BLUE (https://www.youtube.com/playlist?list=PLyQg3m0a5UitP1lHptwWc_4N2ZkRQBfrZ) | ||
+ | * Video tutorial series by KarmaKilledtheCat (https://www.youtube.com/playlist?list=PL0o3fqwR2CsWg_ockSMN6FActmMOJ70t_) | ||
+ | * Video tutorial series by Brian Burton (https://www.youtube.com/playlist?list=PLxgtJR7f0RBKGid7F2dfv7qc-xWwSee2O) | ||
+ | |||
+ | |||
+ | '''PREV.''': [[Your first code]]<br/> | ||
+ | '''NEXT''': [[Classes in Gideros]] | ||
+ | |||
+ | |||
+ | {{GIDEROS IMPORTANT LINKS}} |
Latest revision as of 22:13, 18 November 2023
The Ultimate Guide to Gideros Studio
Overview
Lua was created in 1993 by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, and Waldemar Celes, members of the Computer Graphics Technology Group at PUC-Rio, the Pontifical University of Rio de Janeiro. Lua is used in commercial applications, including Adobe's Photoshop Lightroom, World of Warcraft, Far Cry, Garry's Mod, Supreme Commander and Sonic the Hedgehog. The advantages of Lua are its simplicity and its speed. Lua is mentioned as the fastest language among all interpreted scripting languages.
In Gideros Studio, you usually write your code in Lua however, you can also extend your applications with other languages like Objective-C, C++, C or Java. Gideros Studio, with Lua, produces fast and lightweight applications and games.
Despite the size of the Lua interpreter (about 150Kb in size), Lua is very rich and can be extended.
Let’s start with the simplest Lua code which is printing to the console:
print ("Hello, Gideros Studio")
Here, print(...) is a Lua function. We invoke a function using parenthesis. Print() takes one argument which is the text between double quotes. The code writes "Hello, Gideros Studio" to the standard output (the console).
We use "--" to start a comment, or use --[[ to start a multi-line comment. Consider this example:
-- this is a single line comment
--[[ and this is a multi-line comment
it goes on and on...
and on...
]]
Assignments and variables
Lua is a dynamically-typed language, that is, variables don't have types, only values. All values carry their own type. Consider the following as an example:
MyTable = wide
width = 3; height = 1.5
area = width * height
print (area)
You can nest several lines using semicolons, but it’s optional and generally not a good programming habit as it decreases the readability of your code. Be careful not to use reserved words. Gideros Studio will complain when you use a reserved word in Lua. Consider this example:
local = 1 -- this will not compile because local is reserved.
Since Lua is a small language, it has only 8 basic data types:
- nil (null)
- Booleans
- Numbers
- Strings
- Functions
- Userdata
- Threads
- Tables
Global variables in Lua don't need to be declared. If you want to declare a global variable, just assign it a value. Giving a variable a nil value removes it. When a variable is removed and is not referenced by any other variables, it is cleared from memory thanks to Lua garbage collector.
Lua can support multiple assignments:
a, b, c = 1, 2, 3
-- Easy method to swap two variables
x, y = y, z
-- Function can return multiple values
n, m = calculate (phi, zeta)
Control structures
Lua provides a strong control mechanism between different variable types. As an example of logical decisions, consider the following:
Logical decision | Meaning |
---|---|
A and B | If A is true, then return the result of expression B.
If A is false, then return A |
A or B | If A is true, then return the result of expression A
If A is false, then return B |
Arrays
Arrays can be indexed not only with numbers but with any values. Arrays do not have to be defined a size, they grow as needed. When an array is defined, and you try to reach a value exceeding its boundary, you get a nil value.
Arrays can have any index, e.g. you can start an array with -5, 0 or 1 - it’s all up to you. Consider the following example:
-- creates an array with indices from -2 to 2
array = {}
for i=-2, 2 do
array[i] = "apples"
end
Lua also supports multi-dimensional arrays, which we call "matrices". The statement below:
animals = {"giraffe", "boar", "wolf", "frog"}
is equal to:
animals = {}
animals[1] = "giraffe"; animals[2] = "boar"
animals[3] = "wolf"; animals[4] = "frog"
Similar array syntax can be used to create string lists.
Linked lists
Lua tables (arrays)
In Lua, Tables and Arrays are terms used interchangeably, However, this is true to an extent.
Basic functions
Lua has the ability to call its own functions or C functions, and it handles functions as a "data type". Note that functions can return multiple values, and you can use multiple assignments to collect these functions.
function calculate(w, d, h)
if h > 4 then
print ("The height of the room cannot be more than 4.")
return
end
volume = w * d * h
return volume
end
-- calling the function to calculate the volume of the room
print( calculate(4,3,4) )
Try the function above, this time with values 4,5,5.
String manipulation
Lua has a powerful set of string manipulation functions, such as finding and extracting substrings. Unlike C, the first character of a string has an index of 1 by default.
Note that the string library assumes one-byte character encoding.
The following table gives an overview of string manipulation commands. Have a look at them, and then study the examples below.
String command | Meaning |
---|---|
string.byte (s [, i [, j]]) | Returns the internal numerical codes of the characters s[i], s[i+1], ···, s[j]. The default value for i is 1; the default value for j is i. |
string.find (s, pattern [, init [, plain]]) | Looks for the first match of pattern in the string s. If it finds a match, then find returns the indices of s where this occurrence starts and ends; otherwise, it returns nil |
string.format (formatstring, ···) | Returns a formatted version of its variable number of arguments following the description given in its first argument (which must be a string). |
string.gmatch (s, pattern) | Returns an iterator function that, each time it is called, returns the next capture from pattern over string s. |
string.len (s) | Returns the length of the string s. |
string.lower (s) | Changes all characters of a string to lowercase. |
string.upper (s) | Changes all characters of a string to uppercase. |
string.reverse (s) | Returns a string which is the reverse of the string s. |
Mathematical functions
File I/O
In an app, there always comes a point where you might want to persist some data. Persisting data in simple terms means retaining the data even after the app is exited. Some examples would include the current level, a high score, the player‘s name and so on. There could be other more complex examples where one might have levels stored in files. For all of these, there’s an API, the File I/O API.
There are two ways to reference these functions, the explicit and the implicit. Explicit is when the file to be worked on is specified, it is seen in the form of file: type commands. Implicit is passing the fileHandle to the function for file operations and are generally seen starting with io. Example:
local playerName = "Player1"
local file = io.open ( "settings.save" , "w+" )
file:write( "Playername:" , playerName )
file:close()
This has just saved a file called settings.save with the values "Playername: Player1".
Now that we have some data saved in the file, let us try to read those from the file.
local playerName = nil
local file = io.open (“settings.save”, “r”)
playerName = file:read()
file:close()
print ( “The player name saved is: ”, playerName)
Opening a file
To open a file using Lua, we would use:
file = io.open ( filename [, mode])
Where filename is the path to a file stored on your system. The variable file is passed a reference to the filehandle. The modes for opening the file can be one of the following:
- “r” read mode, this is the default mode
- “w” write mode
- “a” append mode
- “r+” update mode, all previous data is preserved
- “w+” update mode, all previous data is erased
- “a+” append update mode, previous data is preserved, writing is allowed only at the end of the file.
There is no explicit way to open a file, all files are opened using the io.open function.
Closing a file
To close a file, we would use:
io.close([file])
This closes the filehandle passed to the function. If nothing is passed, the default output file is closed. The explicit way to close a file is file:close()
Getting data / Input
In a complex application, we need to get several kinds of data from files stored on the device. In order to get data from a file, we are given a couple of options, including lines and read.
When we use the lines function, it offers an iterator function, which returns a new line from the file each time it is called. Therefore this is used as:
for line in io.lines(filename) do … end
Note that this function does not require the file to be opened or closed, all of that is managed by the function internally.
There is an explicit version of the same, which is used as file:lines(), the difference between this and the implicit function is that to get the filehandle, we have to open the file, there is no parameter passed to the lines function and when the end of file is reached, the file is not automatically closed, we have to close it.
The other way to get data from a file is the read function, which is used as:
data = io.read(format1, …)
The explicit equivalent is: file:read( format1, … )
Since the function does not have a filehandle passed to it, io.read reads from the standard input.
The formats that can be used for the function are:
- “*n” this will read n number of characters from the file.
- “*a” this will read all the data in the file, starting from the current position.
- “*l” this will read the next line, this is the default for this command.
Temporary files
Sometimes there might be the need to create a temporary file either to transfer portions of data or to save a backup copy. The easiest way to create one is to use the io.tmpfile() function. This returns a filehandle to a temporary file opened in update mode and this file is automatically removed when the app terminates.
Writing data
To write data to a file, it has to be opened in a mode that supports writing data. The only way to write data is to use the function write:
io.write(value1, …)
The data passed to the function is written to the file, however since there is no filehandle passed to this function, it writes to the standard output, so if you want to write to a file, the file:write(value1, ...) function should be used.
Seeking your position in the file
Updating data is one of the easiest and one of the most difficult task. An easy way to update data is by positioning the write head and then write the data there. We can get the position and set the position by using the function seek, this is an explicit function only.
position = file:seek( [mode] [,offset] )
By default, the mode is set to “cur” and the offset to 0. Three modes are available:
- “set”: this is from the 0 position (start of file) and the head is moved to the position as specified by offset
- “cur”: this is from the current position, so the offset is added to the current position
- “end”: this is from the end of the file
To get the size of the file, one can use:
filesize = file:seek(“end”)
Set can be used to rewind the reading.
Buffering
The buffering for a file can be set by the file:setvbuf() command which is defined as:
file:setvbuf( mode [, size] )
Three modes are available for this command:
- “no”: There is no buffering, the results are instantaneous
- “full”: The output operations occur only when the buffer is full, or when you the flush command
- “line”: The output is buffered on a per line basis (till a newline character is received)
The size is the size of the buffer in bytes.
Flushing data
If you are using a buffer for writing operations, you might want to flush i.e. commit the file’s output buffer and save all of that to the file. Buffering is not really required in modern systems but still can be helpful at times. So before closing a file, if buffering was on, it is a good practice to flush and ensure that all the data was saved from the buffers to the file. This is an explicit function and is defined as:
file:flush()
There are no parameters passed to this function.
Validating the file operations
While working with files there are a lot of situations that could lead to errors, so it is very important to check for the return values. In general, the return value is nil when the end of the file was reached or a file could not be opened, etc.
To check the state of a file passed with a filehandle we use the type function as:
result = io.type(fileHandle)
The returned value is either “file” if the file is open, “closed” if the file is closed or nil if the fileHandle was not found.
Lua resources
This guide is by no means complete, and we suggest you have a good book covering Lua. There are many resources on the net, and here are a few of them:
- Official Lua Reference Manuals: https://www.lua.org/manual/
- Programming in Lua, second edition by Roberto Ierusalimschy (http://www.inf.puc-rio.br/~roberto/pil2/)
- Learn Lua in 15 minutes (http://tylerneylon.com/a/learn-lua/)
- Litt's Lua Laboratory: The 10% you need for 90% of your work (http://www.troubleshooters.com/codecorn/lua/index.htm)
- Official Lua FAQ (http://www.lua.org/faq.html)
- Classic data structures and algorithms in Lua (https://github.com/EvandroLG/computer_science_in_lua)
- Very nice Lua reference manual helper (http://pgl.yoyo.org/luai/i/_)
- Lua cheatsheet (https://devhints.io/lua)
- Lua C API short reference (http://www.ewald-arnold.de/lua/lua-apiref.pdf)
- Lua: Good, bad, and ugly parts (http://notebook.kulchenko.com/programming/lua-good-different-bad-and-ugly-parts)
- Introduction to Lua programming (http://w3.linux-magazine.com/issue/45/Lua_Programming.pdf)
- Lua performance optimization tips (https://springrts.com/wiki/Lua_Performance)
- Lua video tutorial series by CODE BLUE (https://www.youtube.com/playlist?list=PLyQg3m0a5UitP1lHptwWc_4N2ZkRQBfrZ)
- Video tutorial series by KarmaKilledtheCat (https://www.youtube.com/playlist?list=PL0o3fqwR2CsWg_ockSMN6FActmMOJ70t_)
- Video tutorial series by Brian Burton (https://www.youtube.com/playlist?list=PLxgtJR7f0RBKGid7F2dfv7qc-xWwSee2O)
PREV.: Your first code
NEXT: Classes in Gideros