Input and output

A model communicates with the outside world, e.g. screen and files, by the use of read statements for input of data, and write statements for output of data.

The read function

Data can be read from the command line or from a file by read functions. A read function requires a type value for each parameter to be read. An example:

int i; string s;

i = read(int);
s = read(string);

Two values, an integer value and a string value are read from the command line. On the command line the two values are typed:

1 "This is a string"

Variable i becomes 1, and string s becomes "This is a string". The double quotes are required! Parameter values are separated by a space or a tabular stop. Putting each value on a separate line also works.

Reading from a file

Data also can be read from files. An example fragment:

type row = tuple(string name; list int numbers);

file f;
int i;
list row rows;

f = open("data_file.txt", "r");
i = read(f, int);
rows = read(f, list row);
close(f)

Before a file can be used, the file has to be declared, and the file has to be opened by statement open. Statement open has two parameters, the first parameter denotes the file name (as a string), and the second parameter describes the way the file is used. In this case, the file is opened in a read-only mode, denoted by string "r".

Reading values works in the same way as before, except you cannot add new text in the file while reading it. Instead, the file is processed sequentially from begin to end, with values separated from each other by white space (spaces, tabs, and new-lines). You can read values of different types from the same file, as long as the value in the file matches with the type that you ask. For example, the above Chi program could read the following data from data_file.txt:

21
[("abc", [7,21]),
 ("def", [8,31,47])]

After enough values have been read, the file should be closed with the statement close, with one parameter, the variable of the file. If a file is still open after an experiment, the file is closed automatically before the program quits.

Advanced reading from a file

When reading from a file, the eof and eol functions can be used to obtain information about the white space around the values.

  • The eof (end of file) function returns true if you have read the last value (that is, there are no more values to read).

  • The eol (end of line) function returns true if there are no more values at the current line. In particular, the eol function returns true when the end of the file has been reached.

These functions can be used to customize reading of more complicated values. As an example, you may want to read the same list row value as above, but without having all the comma’s, quotes, parentheses, and brackets of the literal value [("abc", [7,21]), ("def", [8,31,47])]. Instead, imagine having a file clean_data.txt with the following layout:

abc 7 21
def 8 31 47

Each line is one row. It starts with a one-word string, followed by a list of integer numbers. By using the eof and eol functions, you can read this file in the following way:

file f;
list row rows;
string name;
list int xs;

f = open("clean_data.txt", "r");
while not eof(f):
    name = read(f, string);
    xs = <int>[];
    while not eol(f): # Next value is at the same line.
        xs = xs + [read(f, int)];
    end
    rows = rows + [(name, xs)];
end
close(f);

Each line is processed individually, where eol is used to find out whether the last value of a line has been read. The reading loop terminates when eof returns true.

Note that eol returns whether the current line has no more values. It does not tell you how many lines down the next value is. For example, an empty line inserted between the abc 7 21 line and the def 8 31 47 line is skipped silently. If you want that information, you can use the newlines function instead.

The write statement

The write statement is used for output of data to the screen of the computer. Data can also be written to a file.

The first argument of write (or the second argument if you write to a file, see below) Is called the format string. It is a template of the text to write, with 'holes' at the point where a data value is to be written.

Behind the format string, the data values to write are listed. The first value is written in the first 'hole', the second value in the second 'hole' and so on. The holes are also called place holders. A place holder starts with % optionally followed by numbers or some punctuation (its meaning is explained below). A place holder ends with a format specifier, a single letter like s or f. An example:

int i = 5;

write("i equals %s", i)

In this example the text i equals 5 is written to the screen by the write statement. The "i equals %s" format string defines what output is written. All 'normal' characters are copied as-is. The %s place holder is not copied. Instead the first data value (in this case i) is inserted.

The s in the place holder is the format specifier. It means 'print as string'. The %s is a general purpose format specifier, it works with almost every type of data. For example:

list dict(int:real) xs = [{1 : 5.3}];

write("%s", xs)

will output the contents of xs ({1 : 5.3}).

In general, this works nicely, but for numeric values a little more control over the output is often useful. To this end, there are also format specifiers d for integer numbers, and f for real numbers. An example:

int i = 5;
real r = 3.14;

write("Result:\n");
write("%4d/%8.2f", i, r);

This fragment has the effect that the values of i and r are written to the screen as follows:

Result:
   5/    3.14

The value of i is written in d format (as int value), and the value of r is written in f format (as real value). The symbols d and f originate respectively from 'decimal', and 'floating point' numbers. The numbers 4 respectively 8.2 denote that the integer value is written four positions wide (that is, 3 spaces and a 5 character), and that the real value is written eight positions wide, with two characters after the decimal point (that is, 4 spaces and the text 3.14).

A list of format specifiers is given in the following table:

Format specifier Description

%b

boolean value (outputs false or true)

%d

integer

%10d

integer, at least ten characters wide

%f

real

%10f

real, at least ten characters wide

%.4f

real, four characters after the decimal point

%10.4f

real, at least ten characters wide with four characters after the decimal point

%s

character string s, can also write other types of data

%%

the character %

Finally, there are also a few special character sequences called escape sequence which allow to write characters like horizontal tab (which means 'jump to next tab position in the output'), or newline (which means 'go to the next line in the output') in a string. An escape sequence consists of two characters. First a backslash character \, followed by a second character. The escape sequences are presented in the following table:

Sequence Meaning

\n

new line

\t

horizontal tab

\"

the character "

\\

the character \

An example is:

int i = 5, j = 10;
real r = 3.14;

write("Result:\n");
write("%6d\t%d\n\t%.2f\n", i, j, r);

The result looks like:

Result:
     5  10
        3.14

The value of j is written at the tab position, the output goes to the next line again at the first tab position, and outputs the value of r.

Writing to a file

Data can be written to a file, analog to the read function. A file has to be defined first, and opened for writing before the file can be used. An example:

file f;
int i;

f = open("output_file", "w");
write(f, "%s", i); write(f, "%8.2f", r);
close(f)

A file, in this case "output_file" is used in write-only mode, denoted by the character "w". Opening a file for writing destroys its old contents (if the file already exists). In the write statement, the first parameter must be the file, and the second parameter must be the format string.

After all data has been written, the file is closed by statement close. If the file is still open after execution of the program, the file is closed automatically.