CSV Files

A comma-separated values (CSV) file stores tabular data (numbers and text) in plain-text form. Each column in the file is, as the name suggests, separated by commas. JACL supports both the reading and writing of CSV files. By default, CSV files are stored in a directory named data in the same location as the program that is accessing them. For example, the demonstration program contacts.jacl lives in the projects directory and stores its data in projects/data. When referring to a CSV file using any of the commands detailed in this section, there are two ways to specify the filename:

Per-game files — supply a short name without a file extension. This name is used to build the full filename using the following formula:

<DataDirectory>/<ProgramName>-<Name>.csv

For example, the program contacts.jacl stores its contacts in a CSV file called data which translates to the full file:

projects/data/contacts-data.csv

This system ensures that no two JACL programs can overwrite each other's data or access arbitrary files from the filesystem of the webserver.

Global files — supply a filename that includes the .csv extension, enclosed in double quotes. The file is accessed directly from the data directory without any program name prefix:

<DataDirectory>/<Name>

For example, a shared vocabulary file called indonesian_words.csv can be accessed by any program:

iterate "indonesian_words.csv" skip_header
   write field[0] ": " field[1] ^
enditerate

The quotes are required to prevent the dot in the filename from being interpreted as a scope separator.

This is useful for reference data that is shared across multiple programs.

The ITERATE and ENDITERATE Commands

Any ITERATE command must be of the following format:

iterate <FileName> [skip_header]
...
enditerate

The iterate and enditerate commands mark a looping block of code. The block of code is run once for each row in the file specified directly after the iterate command. For example, the +intro function of the program contacts.jacl loops through the file data and looks for the last (highest) ID already assigned to a contact. This number is stored so that the next contact created can be given an ID that one higher, and therefore unique:

# GET THE LAST ID
iterate data
   if field[0] < last_id
      set last_id = field[0]
   endif
enditerate

Each column of the current row being read is loaded into a string array called field. In the case of contacts.jacl, the ID is stored in the first column, or field.

Below is another example of the iterate command, this time it is used to display the entire contents of the file data:

iterate data skip_header
  set INDEX = 0
  while INDEX < field_count
    write field[INDEX]
    write ", "
    set INDEX + 1
  endwhile
  write "^"
enditerate

There are two points of note with this second example. The first is that the optional flag skip_header has been specified after the file name. This flag tells the iterate command to ignore the first line of the file as it contains column headers, not data. For example, a CSV such as this:

ID    First name        Last name
0     Stuart            Allen
1     Fred              Fargnargle
2     Zaphod            Beeblebrox

The second is the constant field_count. This constant contains the number of columns read for the current row. JACL supports a maximum of twenty columns for any given row.

The UPDATE, ENDUPDATE and INSERT Commands

Any update command must be of the following format:

update <FileName>
...
endupdate

Any insert command must be of the following format:

insert <Field0> [<Field1>...]

The update is used to re-write a CSV and therefore provide the opportunity to edit the contents. Below is an example of its use from the +update function of contacts.jacl

update data
   if field[0] = $integer
      insert field[0] firstname_data surname_data email_data home_phone_data mobile_phone_data address_data
   else
      insert field[0] field[1] field[2] field[3] field[4] field[5] field[6]
   endif
endupdate
write "Record " $integer " updated.^^"

This function uses the update command to modify the details for the contact with the ID (field[0]) that is equal to $integer. This is achieved by looping through each line of the file (using the update command) and then checking if the current line is the line that is to be edited. If so, the new values are written out (using the insert command). If not, the existing values are written back to the file (also using the insert command.)

The update and insert commands are also used to delete a record in contacts.jacl. This is achieved by inserting all the existing rows from within an update loop except the record to be deleted:

update data
   if field[0] != $integer
      insert field[0] field[1] field[2] field[3] field[4] field[5] field[6]
   endif
endupdate
write "Record " $integer " deleted.^^"

The APPEND Command

Any append command must be of the following format:

append <FileName> <Field0> [<Field1>...]

The append command is used to write a single extra row to the end of a CSV file. If the CSV file doesn't already exist it will be created and the appended row written to the new file. Below is an example of its use from the +add_record function of contacts.jacl:

set last_id + 1
append data last_id firstname_data surname_data email_data home_phone_data mobile_phone_data address_data
write firstname_data " " surname_data " added to database.^^"

The APPEND_FC, APPEND_NT and APPEND_LC Commands

These three commands are used together to build a single CSV row incrementally across multiple commands. This is useful when the number of columns in a row is not known in advance, such as when iterating over a variable-length data set.

The commands use the following format:

append_fc <FileName> <Field0> [<Field1>...]
append_nt <FileName> <Field0> [<Field1>...]
append_lc <FileName> <Field0> [<Field1>...]

The append_fc (first column) command starts a new line in the CSV file and writes the specified fields. It does not terminate the line, leaving it open for further fields to be added.

The append_nt (no terminate) command continues writing fields to the current open line without terminating it. This command can be called multiple times to add additional columns.

The append_lc (last column) command continues writing fields to the current open line and then terminates the line with a newline character, closing the file.

Below is an example of these three commands being used together to build a row with a variable number of columns:

append_fc results date time name
iterate data skip_header
   if COUNT < obs_count
      append_nt results observations[INDEX]
      set COUNT + 1
   else
      append_lc results observations[INDEX]
   endif
enditerate

In this example, append_fc starts the row with three fixed columns. The iterate loop then adds a variable number of observation columns using append_nt, with the final column being written using append_lc to close the line.

Back to Contents