Prev: Combining SMS and User Interface
Reading and Writing Files
So far, we have an SMS service which automatically responds to incoming messages, and allows the keywords and messages to be edited. But our script is not perfect: whenever it stops, all changes to the database are lost. We must make our database persistent, so it is still around when we restart the script, even after turning off the phone.
To persistently save data, our phone offers a file system. This is very similar to file systems on other computers, be it Windows® or a UNIX®-like system. The main difference is that by far the most common media to store files on larger computers are hard disks, whereas your phone most likely uses memory chips, but this doesn't matter at all. The idea of the file system remains the same.
As on Windows, the file system is organized into drives with directories (folders) and subdirectores. Each file has a name which must be unique to its directory. Section * (Library) tells you more about it.
To access a file from m, module module io is used. Using this module, a function save() saving our database to a file could look as follows:
use io
function save(table, file="table.dat")
f=io.create(file);
for k in keys(table) do
io.writeln(f, k);
io.writeln(f, table[k])
end;
io.close(f)
end
|
Some explanations:
If we execute the following code:
the file table.dat may contain this (use a shell session to easily type the contents of a file):
m>type table.dat
→ party
The party starts at 8pm!
place
I am at home.
mood
Just don't ask.
|
A function load() to read this data back in could look as follows:
function load(file="table.dat")
table=[];
try
f=io.open(file);
k=io.readln(f);
while k#null do
table[k]=io.readln(f);
k=io.readln(f)
end;
io.close(f)
catch e by end;
return table
end
|
This function is slightly more complicated:
- We start with an empty array, and assign it to local variable table.
- io.open(file) opens an existing file to read it and, like io.create(), returns a handle to it. However, if the file doesn't exist, it throws ErrNotFound. To cope with this case, the call to io.open() is put into a try-catch-end block: within such a block, any exception thrown will be catched, and execution continues with the statements between catch and end. Here, there are no such statements, so table remains empty if io.open() fails. This is exactly what we want.
- If io.open() is successful, we read the first line from the file using io.readln(f), which must be a keyword. io.readln() returns null if there is no more data, so we can use a while loop to go through all keywords.
- Inside the while loop, the next line is assigned to table[k], appending an element with the key k to our table, followed by reading the next keyword.
- After all lines have been read, the file is closed with a call to io.close().
- return table returns the contents of variable table as the function result. To load our database from default file table.dat, we simply call load() and assign its result to our variable db:
The two functions
load() and
save() we have presented above do their job nicely. But there is a small problem: our reader assumes the both keywords and replies fit on exactly one line. However, in our
edit() function, we explicitly allowed replies to cover multiple lines.
To solve this problem, we could add a special separator token marking the end of a line, making load() considerably more complicated. But m offers a much simpler solution: the two functions io.readm() and io.writem() allow to write (almost) any m value directly to a file, and read it back in, all in one go. io.writem() not only writes the data, but also information about its type, the length of arrays, their keys, etc. io.readm() uses this information to reconstruct the value from the file[4].
The disadvantage is that the file written is no longer a simple text file you can edit yourself. Instead, it is a binary file highly sensitive to changes, so it is best to treat such files as a black box.
With these two functions, saving and loading becomes particularly easy:
use io
function save(table, file="table.dat")
f=io.create(file);
io.writem(f, table);
io.close(f)
end
function load(file="table.dat")
try
f=io.open(file);
table=io.readm(f);
io.close(f);
return table
catch e by
return []
end
end
|
- save() now just creates the file, writes table, and closes the file again.
- load() does exactly the opposite, and returns the data read. If the file does not exist or cannot be read, this is catched by the try-catch block, and we return an empty array: return [].
Next: Making it a Module© 2004-2011 airbit AG, CH-8008 Zürich
Document AB-M-TUT-887