L3DT development blog
Large 3D terrain generator

Writing and reading files in ZeoScript

Hi All,

In today's instalment of what may be the world's most sporadic weblog I'll discuss some of the new capabilities of L3DT's scripting language, ZeoScript. In particular, I'm going to show you how to use the new file input/output functions in ZeoScript to read and write files, and I'll compare and contrast with the equivalent C code, with which you may be more familiar.

Creating a file

Firstly, let's look at how to create files. In both C and ZeoScript you start by declaring a file handle, which is a 'voidptr' in ZeoScript and a 'FILE*' in C:

ZeoScript new voidptr “fp”
C equivalent FILE* fp;

To open a file in ZeoScript we use the 'fopen' function, just like in C:

ZeoScript set fp <zs:fopen “c:\\example.txt” “r”>
C equivalent fp = fopen(“c:\\example.txt”, “r”);

The ZeoScript code above may look a little strange at first. To explain the differences, I shall now digress to discuss ZeoScript syntax:

A digression on ZeoScript syntax

Functions, not operators

Note in the previous example that ZeoScript doesn't have a '=' (assignment) operator, so instead we use the 'set' function. Indeed, in general ZS uses functions instead of operators. The table below lists some common C operators and their equivalent ZeoScript functions:

C operation ZS function
= set
+ add
- sub
/ div
* mul
% mod
++ incr
== iseq
|| or
&& and
! not
< islt
> isgt

As an example, below is the code to add two numbers “a” and “b”, and assign the result to “c”:

C c = a + b
ZS set c <zs:add a b>

The reason ZS uses functions instead of operators is so that the script interpreter doesn't have to deal with operator precedence. In the future I may add support for C-style operators in ZeoScript, but now is not that time.

Function nesting

Another thing to note in the previous examples is that nested function calls in ZS look a little different to those in C or other languages. To call a function from within the argument list of another function, you have to use the '<zs:…>' operator, like so:

ZS foo <zs:bar>
C equivalent foo(bar());

So, returning to a previous example of opening a file:

ZS set fp <zs:fopen “c:\\example.txt” “r”>

Here the 'fopen' function is being called, and it's return value is being passed to the 'set' function. The literal equivalent in C, assuming it too used the 'set' function, would be:

C equivalent set(fp, fopen(“c:\\example.txt”, “r”));

Okay, digression over. Back to file input/output in ZeoScript:

Reading from a file

To read from a file in ZeoScript we use the 'fread' function, just like we do in C.

In C:

int i;
fread(&i, sizeof(int), 1, fp);

In ZS:

new int "i"
fread fp i

Note here that in ZeoScript you don't have to explicitly tell 'fread' how many bytes to read, since it can work this out from the size of the variable you ask it to read. For example, if you ask fread to read a 32 bit integer (int), it will read 4 bytes, and if you ask it to read a double-precision floating point number (double), it will read 8 bytes.

Writing to a file

In the C example below, I declare a string and write it to a file using 'fwrite':

const char* s = "This is a string";
fwrite(s, strlen(s), 1, fp);

In ZeoScript we also use 'fwrite':

new string "s"
set s "This is a string"
fwrite fp s

Again, note that we don't have to tell fwrite how many bytes to write; it works it out from the string length.

Closing files

Closing files is trivial in both C and ZS:

C fclose(fp);
ZS fclose fp

File save dialog box

Another common file-related operation is asking the user to choose a filename using a Windows 'Save as' dialog box. The code to do this in plain old C is hideous, but in the Microsoft flavour of C++ it is:

CString FileName = _T("");
CFileDialog dlg(FALSE, _T("txt"), NULL, 4|2, _T("Text files (*.txt)|*.txt|"), NULL, NULL);
if(IDOK!=dlg.DoModal()) { // exit if user hit cancel
  return false;

In ZeoScript we have built-in functions called 'file.OpenDlg' and 'file.SaveDlg' for opening 'Open' and 'Save as' file dialog boxes. These functions return a string containing the file name. So, the ZS equivalent of the above C++ code is:

new string "FileName"
set FileName <zs:file.SaveDlg "txt" NULL "Text files (*.txt)|*.txt|All files (*.*)|*.*|">
if <zs:iseq 0 <zs:strlen FileName>> // exit if name is zero-length (user hit cancel)
  return false

Example 1: writing a text file

The script example below puts all these elements together to do the following:

  • Asking the user for a filename using a 'Save as' dialog box.
  • Asking the user for some text to write to the file.
  • Creating the file, writing the user's text into it, and closing the file.
// ask user for filename
new string "FileName"
set FileName <zs:file.SaveDlg "txt" NULL "Text files (*.txt)|*.txt|All files (*.*)|*.*|">
if <zs:iseq 0 <zs:strlen FileName>>
  return 0

// Ask user for text to write to file
new string "s"
if <zs:not <zs:EditUI s "Enter file text">>
  return 0

// Open file
new voidptr "fp"
set fp <zs:fopen FileName "w">
assert fp "Cannot open file!"

// Write text to file
fwrite fp s

// Close file
fclose fp

Example 2: reading a text file

Okay, how about reading the file? In the example below we'll do just that, using the 'fread' function to read the file character-by-character, and the 'strcat' function to append each character to a string to collect the results.

// Ask user for filename
new string "FileName"
set FileName <zs:file.OpenDlg "txt" NULL "Text files (*.txt)|*.txt|All files (*.*)|*.*|">
if <zs:iseq 0 <zs:strlen FileName>>
  return 0

// Open file
new voidptr "fp"
set fp <zs:fopen FileName "r">
assert fp "Cannot open file!"

// Prepare some variables
new char "c"
new string "s"
new bool "FileDone"
set FileDone false

// Read file character-by-character, and add to end of string using 'strcat'
// (note this is rather inefficient, but if you need to read large files quickly,
//  then you should be writing a plugin in C++, rather than a script in ZS!)

  if <zs:fread fp c>
    set s <zs:strcat s c>  // append character to end of string
    set FileDone true      // fread failed, so we're at the end of the file.
while <zs:not FileDone>

// Write file contents (in string) to event log
echo s

// Report file length using ftell
echo <zs:strcat <zs:strcat "File length was " <zs:ftell fp>> " bytes">

// Close file
fclose fp

Final thoughts

In previous releases of L3DT, ZeoScript did not support general-purpose programming features, and so was suitable for only a very narrow range of tasks. However, the next release of ZeoScript is far less specialised, and will include all the building blocks that script writers will need to do useful things. In this post I've shown you how to read and write simple files. Over the next few days and weeks I'll demonstrate some more advanced examples of ZeoScript use. Please stay tuned.

Cheerio, Aaron.

l3dt/2009/jan/28.txt · Last modified: 2017/08/31 04:19 (external edit)
Except where otherwise noted, content on this wiki is licensed under the following license:CC Attribution-Share Alike 3.0 Unported
L3DT Development Blog RSS Donate Powered by PHP Valid XHTML 1.0 Valid CSS Driven by DokuWiki