
============================================================================
Pure-CSV - Comma Separated Value Interface for the Pure Programming Language
============================================================================

:Author: Eddie Rucker <erucker@bmc.edu>
:Date: |date|
:Copyright: 2008, 2009 by Eddie Rucker
:License: 3-clause BSD license, see the accompanying COPYING file for details

.. |date| date::

The CSV library provides an interface for reading and writing comma 
separated value files that is loosely based on Python's CSV module 
(http://docs.python.org/lib/module-csv.html).

.. contents::
.. sectnum::

Installation
------------

Run ``make`` to compile the module and ``make install`` (as root) to 
install it in the Pure library directory. This requires GNU make. The 
``make install`` step is only necessary for system-wide installation.

The ``make`` utility tries to guess your Pure installation directory 
and platform-specific setup. If it gets this wrong, you can set some 
variables manually. In particular, ``make install prefix=/usr`` sets 
the installation prefix, and ``make PIC=-fPIC`` or some similar flag 
might be needed for compilation on 64 bit systems. Please see the 
Makefile for details.

Usage
-----

Data records are represented as lists of strings, integers, and 
doubles. Three predefined dialects are provided; RFC4180 (default for
Windows), UNIX (default for Linux, \*BSD, and Unix), and EXCEL; and
procedures are provided to create other CSV dialects. See
(http://www.ietf.org/rfc/rfc4180.txt) for more details about the 
RFC4180 standard.

Examples of reading and writing CSV files are provided in the file
"samples.pure" located in the examples subdirectory.

Handling Errors
~~~~~~~~~~~~~~~

.. _error:

``error msg``
  is an error handling term. Input that does not abide by dialect 
  rules or records containing field types other than strings, integers, 
  and doubles produce a special ``csv::error msg`` term, where ``msg`` 
  is a string describing the particular error. Your application should 
  either check for these or have ``csv::error`` defined to directly 
  handle errors in some way (e.g., provide a default value, or raise an 
  exception).

.. _example_error:

Examples

::
    
  > using system, csv;
  > csv::str [1%4, 1%3]; // rational numbers are not allowed
  csv::error "field 1: invalid conversion type."
  > /* Now, print a message and
  >    perform an exit action on the error */
  > csv::error msg = printf "CSV Error: %s\n" msg $$ exit 0;
  > csv::str [1%4, 1%3];
  CSV Error: field 1: invalid conversion type.
  $

Creating Dialects
~~~~~~~~~~~~~~~~~

.. _dialect:

``dialect list``
  creates a dialect from a list of dialect option pairs. Option pairs 
  are denoted ``a=>b`` where ``a`` is the option and ``b`` is the 
  option's value. The list of possible options and option values are 
  presented below.

  - ``DELIMITER`` - Field delimiter.

    * Value - any string.
    * Default - ``","``.

  - ``ESCAPE`` - Embedded escape character.

    * Value - any string.
    * Default - ``"\""``.
    * Notes - When reading, the escape character is dropped and the next 
      char is inserted into the field. When writing, the escape 
      character is written into the output stream.
  
  - ``QUOTE`` - Quote character.
  
    * Value - any string.
    * Default - ``"\""``.
    * Notes - If embedded quotes are doubled, ``ESCAPE`` must equal the 
      value of ``QUOTE``. The dialect function will automatically set 
      the ``ESCAPE`` string to that of ``QUOTE`` if ``ESCAPE`` is not 
      specified in the dialect argument list.
  
  - ``QUOTING_STYLE`` - Sets the quoting style of strings and/or 
    numbers.

    * Value - One of {``QUOTE_ALL``, ``QUOTE_STRINGS``, 
      ``QUOTE_EMBEDDED``}.
    * Default - ``QUOTE_STRINGS``.

  - ``LINETERMINATOR`` - Record termination value.

    * Value - any string.
    * Default: ``"\r\n"``.

  -  ``SKIPSPACE`` - Skip leading white space flag.

    * Value - boolean {1=true, 0=false}.
    * Default - 1.
    * Note - Quoted fields always retain white space.

  The following example illustrates the construction of a dialect for 
  reading tab delimited files without quoted strings.

.. _example_dialect:

Examples

::
    
  > using csv;
  > /* Use the predefined EXCEL dialect */
  > csv::list ("\"=\"\"004\",23\r\n", csv::EXCEL);
  ["=\"004",23]
  > const d = csv::dialect [
  >   csv::DELIMITER => "\t", 
  >   csv::QUOTING_STYLE => csv::QUOTE_EMBEDDED];
  > csv::list ("\"John\tSmith\"\t23\t4.5\tfreshman", d);
  ["John\tSmith",23,4.5,"freshman"]
  >

String and List Functions
~~~~~~~~~~~~~~~~~~~~~~~~~

.. _str:

``str list|(list, dialect)``
  converts a list to a CSV formatted string. If the argument is a list,
  ``str`` converts the list using RFC 4180 rules 
  (http://www.ietf.org/rfc/rfc4180.txt). If the argument is given as a 
  tuple, then ``str`` converts the list using the rules specified in the 
  user's dialect. A ``csv::error msg`` term is invoked if a fields 
  contains data other than a string, integer, or a double types.

.. _examples_str:

Examples

::
    
  > using csv;
  > csv::str [1,"",3.00,"bob \"plays drums\""];
  "1,\"\",3,\"bob \"\"plays drums\"\"\"\n"
  > const d = csv::dialect [csv::DELIMITER => "\t"];
  > csv::str ([1,"",3.00,"bob \"plays drums\""], d);
  "1\t\"\"\t3\t\"bob \"\"plays drums\"\"\"\n"
  >     

.. _list:

``list string|(string, dialect)``
  creates a list of string or numeric fields from a CSV formatted
  string. If the argument is given as a string, then the conversion 
  is performed using RFC 4180 rules 
  (http://www.ietf.org/rfc/rfc4180.txt). If the argument is given as
  a tuple, then the conversion is performed according to the rules 
  specified by the dialect. An invalidly formatted CSV string returns
  a ``csv::error msg`` term.
  
.. _example_list:

Examples

::
    
  > using csv;
  > csv::list "1,\"\",3,\"bob \"\"plays drums\"\"\"\n";
  [1,"",3,"bob \"plays drums\""]
  > const d = csv::dialect [csv::DELIMITER => "\t"];
  > csv::list ("1\t\"\"\t3\t\"bob \"\"plays drums\"\"\"\n", d);
  [1,"",3,"bob \"plays drums\""]
  >

File Reading Functions
~~~~~~~~~~~~~~~~~~~~~~

.. _fgets:

``fgets file|(file, dialect)``
  is equivalent to ``csv::list`` except that reading is from a file. 
  The file must first be opened using ``fopen`` which is defined in the 
  system library (system.pure).

.. _fget:

``fget file|(file, dialect)``
  reads a whole file and returns a list of records. This procedure 
  should only be used on data files that are small enough to fit in the 
  computer's primary memory.

.. _examples_fget:

Examples

::
    
  > using system, csv;
  > /* Write a file to test */
  > let f = fopen "test.csv" "w";
  > csv::fput [[1,"a"], [2,"b"], [3,"c"]] f;
  ()
  > fclose f;
  0
  > let f = fopen "test.csv" "r";
  > csv::fgets f;
  [1,"a"]
  > fclose f;
  0
  >

File Writing Functions
~~~~~~~~~~~~~~~~~~~~~~

When modifying CSV files that will be imported into Microsoft Excel,
fields with significant leading 0s should be written using a 
``"=""0..."""`` formatting scheme. This same technique will work for
preserving leading space too. Again, this quirk should only be
necessary for files to be imported into MS Excel. 

.. _fputs:

``fputs list file|(file, dialect)``
  is equivalent to ``csv::str`` except that writing is to a file. The 
  file must first be opened using ``fopen`` which can be found in the 
  system library.

.. _fput:

``fput list file|(file, dialect)``
  writes a list of records to a file. Each record is converted 
  according to the rules stated in the ``csv::str`` procedure.

.. _examples_fput:

Examples

::
  
  > using system, csv;
  > /* Write a file to test */
  > let f = fopen "test.csv" "w";
  > csv::fput [[1,"a"], [2,"b"], [3,"c"]] f;
  ()
  > fclose f;
  0
  > let f = fopen "test.csv" "r";
  > /* Read the first record */
  > csv::fgets f;
  [1,"a"]
  > /* Read the next record */
  > csv::fgets f;
  [2,"b"]
  > fclose f;
  0
  >
