Prev: Module files: File and Directory Access
Module io: File and Stream Input/Output
This module provides functions to read and write files or communication streams via the underlying operating system.
Some of the functions in this module can render a mobile phone completely unusable, e.g. by overwriting sensitive files. Make sure you regularly back up your mobile phone, and inform yourself how to reset your phone to factory status. You have been warned!
Before file operations can be performed, a file has to be opened for reading or reading and writing. Opening a file returns a stream object which identifies the file for subsequent operations. When file operations are completed, the file should be closed[5].
// open the standard autoexec.m script
f=io.open(system.appdir + "autoexec.m");
// read the first 28 bytes (characters)
s=io.read(f, 28);
print s;
→ /*
Default autoexec script
// close the file
io.close(f)
|
There are two special files:
• const stdin = standard input
Reads from the console.
• const stdout = standard output
Writes to the console[6]
A file always has a character encoding scheme (CES) it uses when reading or writing UNICODE® characters. The following encoding schemes exist:
• const raw = 0
Only the low byte of each character is read or written, the high byte is assumed zero. The number of bytes written corresponds exactly to the number of characters. This is a good CES for reading and writing Latin characters, and the default CES.
• const utf8 = 1
Characters are encoded using UTF-8. This is a compact variable length encoding properly encoding all characters, but the number of bytes written is not easily predictable. Reading with the UTF-8 CES throws ExcInvalidUTF8 if a character sequence not conforming to the UTF-8 standard is encountered.
• const utf16le = 2
Characters are encoded using UTF-16 LE (little endian, low byte first). Each character is read or written as two bytes, the number of bytes written is therefore twice the number of characters.
• const utf16be = 3
Like utf16le, but characters are encoded using UTF-16 BE (big endian, high byte first).
• const bom = 0xfeff
This is a pseudo-encoding scheme which will determine the real scheme to use depending on the next one to three bytes read. These bytes are analyzed whether they form a BOM (Byte Order Mark) in any supported encoding. If there is a BOM, the CES will be set accordingly, and actual reading will start with the data following the BOM. If there is no BOM or the necessary bytes are not available, the CES will be set to raw.
To write a BOM to a stream s in its current encoding scheme, use the following statement:
if io.ces(s)#io.raw then
io.write(s, char(io.bom))
end
|
io.append
• function append(path, ces=io.raw) → Native Object
Permissions: Read+Write(path)
Opens a file to append to it, and returns its stream object. If the file exists, it is opened for read and write access, and the file pointer is set to its end. If the file doesn't exist, this call is equivalent to io.create.
If the file already exists, it is truncated to zero length.
Throws ErrPathNotFound if the directory does not exist.
f=io.append("activity.log");
// file pointer is at the end
print io.size(f), io.seek(f,0,true)
→ 1813 1813
io.close(f)
|
io.avail
• function avail(stream) → Number
Returns the number of bytes which can be read without blocking. For disk files, this is normally the number of bytes to the end of the file.
For io.stdin, this is the number of characters which can be read without changing to input mode, i.e. calling a reading function: console input is normally only accepted during a read on io.stdin (when the
state icon is shown). See ui.keys for information on removing this restriction.
// read all remaining console input
len=io.avail(io.stdin);
s=io.read(io.stdin, len)
|
io.close
• function close(stream) → null
Flushes and closes the file stream. Attempts to close io.stdin or io.stdout are ignored.
See also io.flush.
io.ces
• function ces(stream) → Number
• function ces(stream, scheme) → Number
Gets or sets the character encoding scheme of a file. With one argument, returns the current CES of the file stream. With two arguments, returns the old CES, and sets the CES of stream to scheme.
Throws ErrAccessDenied when attempting to change the CES of io.stdin or io.stdout.
io.copy
• function copy(dst, src, bytes=io.avail(src)) → null
Reads bytes bytes from stream src and writes them to dst. The data is read and written literally, i.e. independent from the CES of the streams.
Throws ErrEof if EOF is reached when reading from src.
Assuming dst and src use the CES io.raw, this is equivalent to, but considerably more efficient than the following m fragment:
io.write(dst, io.read(src, bytes))
|
The copy function is particularly useful to send e.g. a file via a network connection, or to copy the input from a connection to an in-memory stream.
See also module memio.
io.create
• function create(path, ces=io.raw) → Native Object
Permissions: Write(path)
Creates a new, empty file in the directory and with the name specified by path, and returns its stream object. The initial CES is set to ces. The file is opened for read and write access.
If the file already exists, it is truncated to zero length.
Throws ErrPathNotFound if the directory does not exist.
f=io.create("sample.xml", io.utf8);
print io.size(f)
→ 0
io.close(f)
|
io.flush
• function flush(stream) → Boolean
• function flush(stream, auto) → Boolean
With one argument flushes the file stream, i.e. writes any pending data to the underlying file or communication stream, and returns the auto flush state. On seekable streams, it will also make sure that the next read will read data from the underlying stream, not from the buffer.
With two arguments, enables (auto=true) or disables (auto=false) auto flushing, and returns the previous setting.
If auto flushing is enabled, the file will be flushed after each io.write... and io.print... call. For optimum performance when writing a lot of data, auto flushing should be disabled.
If a file has auto flushing enabled, calling io.flush to flush the file is never required.
By default, auto flushing is enabled.
// disable auto flushing before writing a lot of data
old=io.flush(f, false);
for line in lines do
io.writeln(f, line)
end;
// restore the previous auto flush state
io.flush(f, old)
|
io.format
• function format(pattern, ...) → String
Return the formatted string resulting from replacing the %-conversion specifications in pattern by the arguments following it. The specifications indicate how the arguments should be converted into strings. To produce a literal "%" in the result, use "%%" in the pattern.
io.format
supports a subset of ANSI C's printf specifications. The general format of a conversion specification in pattern is:
| | |
Specification := '%' {Flag} [Width] ['.' Precision] Type .
Flag := ' ' | '+' | '-' | '0' .
Width := '*' | Decimal .
Precision := '*' | Decimal .
Type := 'b' | 'c' | 'd' | 'e' | 'f' | 'g' | 'm' | 'o' | 's' |
'u' | 'x' | 'X' | '%' .
|
|
The optional Flags are:
| (space) | When converting signed numbers, use a space character as positive sign prefix (default is no positive sign prefix). |
| + | When converting signed numbers, use a plus character as positive sign prefix (default is no positive sign prefix). |
| - | Left align the converted string (padded on the right). Default is right aligned (padded on the left). |
| 0 | When converting numbers, pad with zeroes (right alignment only). |
The optional Width indicates the minimum width of the converted string. If the conversion result is shorter than the requested width, it is padded with space or zeroes, depending on the alignment.
The optional Precision indicates the number of decimal places (types e and f) or significant digits (type g), or the maximum length of the conversion result (all other types).
If Width or Precision are *, they are read from the argument list (before the argument to be converted).
The supported conversion Types are: [4]
| b | Convert an unsigned integer into a binary number. |
| c | Convert a character code into a single character. |
| d | Convert a signed integer into a decimal number. |
| e | Convert a floating point number in exponential notation. Precision indicates the number of decimal places after the point in the mantissa, is limited by 64, and defaults to 6. |
| f | Convert a floating point number in fixed point notation. Precision indicates the number of decimal places after the point, is limited by 64, and defaults to 6. |
| g | Convert a floating point number in fixed point notation if possible, otherwise in floating point notation. Precision indicates the number of significant digits, is limited by 65, and defaults to 7. |
| m | Generically convert a value into a string (as with .str). |
| o | Convert an unsigned integer into an octal number. |
| s | Use a string as is. |
| u | Convert an unsigned integer into a decimal number. |
| x | Convert an unsigned integer into a hexadecimal number, with lowercase letters a to f. |
| X | Convert an unsigned integer into a hexadecimal number, with uppercase letters A to F. |
| % | No argument is converted, the conversion result is a literal %. |
|
Throws ErrArgument if an invalid format specification is found. Throws ExcWrongParamCount if there are not enough arguments, or if not all arguments are used in the conversion.
a=[140, -150];
print io.format("values=[%d, %d]", a[0], a[1])
→ values=[140, -150]
print io.format("values=[0x%x, 0x%x]", a[0], a[1])
→ values=[0x8c, 0xffffff6a]
for w=20 to 22 do
print io.format("%s=%*m", "values", w, a)
end;
→ values= [140,-150]
→ values= [140,-150]
→ values= [140,-150]
x=8700123.45 eps=1e-3
print io.format("%e %+.1e %.2e", x, x, eps);
→ 8.700123E+06 +8.7E+06 1.00E-3
print io.format("%f %+.1f %.2f", x, x, eps);
→ 8700123.450000 +8700123.5 0.00
print io.format("%g %+.1g %.2g", x, x, eps);
→ 8700123 +9E+06 0.001
|
See also: io.printf
io.open
• function open(path, rw=false, ces=io.raw) → Native Object
Permissions: Read(path) / Read+Write(path)
Opens an existing file in the directory and with the name specified by path, and returns its stream object. The initial CES is set to ces. If rw=false, the file is opened for read access, and attempts to write to it will throw ErrAccessDenied. If rw=true, the file is opened for read and write access.
Throws ErrPathNotFound if the directory does not exist, and ErrNotFound if the file does not exist.
f=io.open("sample.xml", false, io.utf8);
print f
→ stream@41255c
io.close(f)
|
io.print
• function print(stream, expression, ...) → null
Writes a list of expressions as strings to file stream, using the current character encoding scheme. The expressions are converted to strings according to the rules in section * (Reference). The strings are written one after the other, without separators or a terminator string.
old=13;
io.print(io.stdout, "old=", old, ", new: ");
→ old=13, new:
|
io.printf
• function printf(stream, pattern, ...) → null
Writes the formatted string resulting from replacing the %-conversion specifications in pattern by the arguments following it. The string is written using the current character encoding scheme, and produced following exactly the same rules as those of io.format. See there for an explanation of conversion specifications.
Note that io.printf is more efficient than first producing a string with io.format and then writing it. The result however is the same.
Throws ErrArgument if an invalid format specification is found. Throws ExcWrongParamCount if there are not enough arguments, or if not all arguments are used in the conversion.
f=io.create("sample.txt");
x=8700123.45 eps=1e-3
print io.printf(f, "%e %+.1e %.2e\n", x, x, eps);
print io.printf(f, "%f %+.1f %.2f\n", x, x, eps);
print io.printf(f, "%g %+.1g %.2g\n", x, x, eps);
io.seek(f, 0);
s=io.readln(f);
while s#null do
print s; s=io.readln(f)
end;
io.close(f)
→ 8.700123E+06 +8.7E+06 1.00E-3
→ 8700123.450000 +8700123.5 0.00
→ 8700123 +9E+06 0.001
|
io.println
• function println(stream, expression, ...) → null
Like io.print, but also writes a newline (CR and LF characters) after writing all arguments.
io.read
• function read(stream, len) → String|null
Reads from stream until len characters have been read, or the file end has been reached, and returns the characters read as a string.
len determines the number of characters read, not the number of bytes: with encoding schemes different from io.raw, the number of bytes read may be greater than len.
Advances the file pointer by the number of bytes read. Returns null if the file pointer is already at the end of stream. Reading from io.stdin never returns null, as the user is prompted for new data if there is no data to read.
f=io.open("Hello.mp3");
// read first three bytes of MP3 file
print io.read(f, 3);
→ ID3
io.close(f)
|
See also: .code
io.readln
• function readln(stream, len=256) → String|null
Reads from stream until len characters have been read, or until the next end of line has been reached[7], and returns the characters read as a string. The string returned does not contain the end of line mark.
len determines the number of characters read, not the number of bytes: with encoding schemes different from io.raw, the number of bytes read may be greater than len.
Advances the file pointer by the number of bytes read. Returns null if the file pointer is already at the end of stream.
f=io.open(system.appdir + "autoexec.m");
// read the first three lines
for i=1 to 3 do
print io.readln(f)
end
→ /*
Default autoexec script for interactive shells.
(c) 2005 airbit AG, www.airbit.ch
io.close(f)
|
io.readm
• function readm(stream, old3rd=false) → anytype
Reads the next m data item from stream, and returns it. The data must have been written using io.writem.
Advances the file pointer by the number of bytes read.
The current encoding scheme does not affect how the input data is interpreted.
If old3rd=true, data is assumed to be in the (wrong) format written by Symbian 3rd Edition devices with m versions prior to 2.01. Under normal circumstances, this parameter is not used.
Throws ErrEof if end of file is reached during reading. Throws ErrCorrupt if the data in the file is invalid. Throws ExcNoSuchClass if the stream contains an instance of a class which is not loaded, i.e. not known to the current process. Throws ExcUnknownField if a class has less fields than the instance being read.
Care must be taken when reading class instances which were written with a different class definition. Class fields are simply written and read in order declared when the data was written. Fields added to the class since the data was written are set to null.
See io.writem for an example.
io.seek
• function seek(stream, pos, current=false) → Number
Sets the file pointer position of file stream to pos. If current=false, pos is an absolute position and must not be negative. If current=true, pos is relative to the current position and may also be negative.
The file pointer position is always in bytes, independent of the current character encoding scheme.
Returns the new absolute file position.
io.seek(f, 0); // seek to beginning of file
io.seek(f, io.size(f)); // seek to end of file
io.seek(f, -40, true)); // rewind 40 bytes
current=io.seek(f, 0, true); // get current position
|
io.size
• function size(stream) → Number
Returns the size of file stream, in bytes.
See also: files.size
io.timeout
• function timeout() → Number
• function timeout(ms) → Number
Gets or sets the timeout used in reads and writes. Without an argument, returns the current timeout in milliseconds. With one argument, returns the old timeout, and sets the new timeout to ms. Setting the timeout to zero (the default) or a negative value disables timeouts, i.e. I/O operations can block indefinitely.
Throws ExcValueOutOfRange if ms exceeds 2147483 (35 minutes and 47.483 seconds).
The timeout is used in all following reads and writes: whenever an operation does not complete within the given number of milliseconds, it throws ErrTimedOut.
// give the user three seconds to input data
io.timeout(3000);
try
s=io.readln(io.stdin)
// process input
catch e by
// if it wasn't a timeout, rethrow e
if index(e, "ErrTimedOut") # 0 then throw e end;
print "You waited too long..."
end
|
io.wait
• function wait(streams) → Native Object
Waits until at least one stream in the array streams has at least one byte to read from (i.e. io.avail returns a value greater than zero), and returns this stream.
io.wait
is most useful when simultaneously processing several input streams (TCP/IP, Bluetooth, IPC), as it avoids the need for a "busy waiting loop". See also module async.
ipconn=...
btconn=...
case io.wait([io.stdin, ipconn, btconn])
in io.stdin:
// read from the console
in ipconn:
// read from ipconn
in btconn:
// read from btconn
end
|
io.write
• function write(stream, string) → null
Writes the string string to file stream, using the current character encoding scheme.
f=io.create("sample.txt", io.utf8);
s="un château français";
io.write(f, s);
print len(s), io.size(f)
→ 19 21
|
io.writeln
• function writeln(stream, string) → null
Writes the string string, followed by a newline (CR and LF characters) to file stream, using the current character encoding scheme.
f=io.create("sample.txt", io.utf8);
s="un château français";
io.writeln(f, s);
print len(s), io.size(f)
→ 19 23
|
io.writem
• function writem(stream, data) → null
Writes data to file stream, so it can be read back in via io.readm. data can have any m type: number, string, boolean, array, or null. Function references and native objects can neither be written nor read.
If data is an array, elements of it (or its subarrays) which are referenced multiple times are only written once and correctly resolved when they are read back in. This permits to properly write ("serialize") recursive data structures (which in m are always arrays with elements referencing the array itself).
The current encoding scheme does not affect the raw data written.
Throws ErrArgument if data is of a type which cannot be written.
// write a string
data1="Simply a string";
// and a more complex data structure
data2=["One":1, "Two":2.5, false, null, "V":[8,9,10]];
// and a class instance
class C
a b
function init(a, b)
this.a = a; this.b = b
end
end
data3=C("String", 24);
f=io.create("sample.dat");
io.writem(f, data1);
io.writem(f, data2);
io.writem(f, data3);
io.close(f);
// read it back in
f=io.open("sample.dat");
print io.readm(f)
→ Simply a string
a=io.readm(f);
print a, keys(a)
→ [1,2.5,false,null,[...3]] [One,Two,null,null,V]
print io.readm(f)
→ C(a=String,b=24)
io.readm(f)
→ ErrEof thrown
|
Next: Module memio: In-Memory Streams and Buffers© 2004-2011 airbit AG, CH-8008 Zürich
Document AB-M-LIB-888