CSV spreadsheet files are suitable for storing tabular data in a relatively portable way.

The CSV format is flexible but somewhat ill-defined.

For present purposes, authors may assume that the data fields contain no commas, backslashes, or quotation marks.



Task

Read a CSV file, change some values and save the changes back to a file.

For this task we will use the following CSV file:

C1,C2,C3,C4,C5
1,5,9,13,17
2,6,10,14,18
3,7,11,15,19
4,8,12,16,20

Suggestions


 Show how to add a column, headed 'SUM', of the sums of the rows.
 If possible, illustrate the use of built-in or standard functions, methods, or libraries, that handle generic CSV files.



# count occurrances of a char in string #
PROC char count = (CHAR c, STRING str) INT:
   BEGIN
      INT count := 0;
      FOR i TO UPB str DO
	 IF c = str[i] THEN count +:= 1
	 FI
      OD;
      count
   END; 
 
# split string on separator #
PROC char split = (STRING str, CHAR sep) FLEX[]STRING :
   BEGIN
      INT strlen := UPB str, cnt := 0;
      INT len, p;
      INT start := 1;
      [char count (sep, str) + 1] STRING list;
      WHILE start <= strlen ANDF char in string (sep, p, str[start:]) DO 
	 p +:= start - 1;
	 list[cnt +:= 1] := str[start:p-1];
	 start := p + 1
      OD;
      IF cnt = 0 THEN list[cnt +:= 1] := str
      ELIF start <= UPB str + 1 THEN list[cnt +:= 1] := str[start:]
      FI;
      list
   END;
 
PROC join = ([]STRING words, STRING sep) STRING:
   IF UPB words > 0 THEN 
      STRING str := words [1];
      FOR i FROM 2 TO UPB words DO
	 str +:= sep + words[i]
      OD;
      str
   ELSE
      ""
   FI;
 
# read a line from file #
PROC readline = (REF FILE f) STRING:
   BEGIN
      STRING line;
      get (f, line); new line (f);
      line
   END;
 
# Add one item to tuple #
OP +:= = (REF FLEX[]STRING tuple, STRING item) VOID:
   BEGIN 
      [UPB tuple+1]STRING new;
      new[:UPB tuple] := tuple;
      new[UPB new] := item;
      tuple := new
   END;
 
# convert signed number TO INT #
OP TOINT = (STRING str) INT:
   BEGIN
      INT n := 0, sign := 1;
      FOR i TO UPB str WHILE sign /= 0 DO
	 IF is digit (str[i]) THEN n := n * 10 + ABS str[i] - ABS "0"
	 ELIF i = 1 AND str[i] = "-" THEN sign := -1
	 ELIF i /= 1 OR str[i] /= "+" THEN sign := 0
	 FI
      OD;
      n * sign
   END;
 
OP STR = (INT i) STRING: whole (i,0);
 
# The main program #
FILE foo;
open (foo, "CSV_data_manipulation.data", stand in channel);
FLEX[0]STRING header := char split (readline (foo), ",");
header +:= "SUM";
print ((join (header, ","), new line));
WHILE NOT end of file (foo) DO
   FLEX[0]STRING fields := char split (readline (foo), ",");
   INT sum := 0;
   FOR i TO UPB fields DO
      sum +:= TOINT fields[i]
   OD;
   fields +:= STR sum;
   print ((join (fields, ","), new line))
OD;
close (foo)