Differences between revisions 21 and 53 (spanning 32 versions)
Revision 21 as of 2007-12-15 20:38:40
Size: 9145
Editor: KaiJaeger
Comment:
Revision 53 as of 2016-02-02 13:29:14
Size: 9738
Editor: KaiJaeger
Comment: Code improvements as suggested by PierreGilbert
Deletions are marked like this. Additions are marked like this.
Line 2: Line 2:
[[TableOfContents]] <<TableOfContents>>
Line 4: Line 4:
CSV stands for comma separated values. Those files are still used to transport tabular data between applications that are not directly connected. The files can be edited with any spreadsheet application like Microsoft Excel. CSV stands for comma separated values. Those files are still used to transport tabular data between applications that are not directly connected.
Line 6: Line 6:
There are some things one need to know about CSV file in order to deal with them: Such files can be edited with any spreadsheet application like Microsoft Excel.
Line 8: Line 8:
 * Fields are separated by commas There are some things one need to know about CSV files in order to deal with them:

 * Fields are separated by commas. Well, mostly.
Line 12: Line 14:
 * If a field contains either a comma or one of the end line chars, either the char(s) or the whole contents needs to be escaped. Excel escapes these values by embedding the field inside a set of double quotes. For example, a single cell with the text apples, carrots, and oranges becomes "apples, carrots, and oranges"  * If a field contains either a comma or a double quote or one of the end line chars either the char(s) or the whole contents needs to be escaped.
Line 14: Line 16:
For details and background information see http://www.csvreader.com/csv_format.php Excel escapes these values by embedding the field inside a set of double quotes. For example, a single cell with the text `apples, carrots, and oranges` becomes `"apples, carrots, and oranges"`.
Line 16: Line 18:
Note that the format comes with a nasty built-in-problem: there is no way to recognize a cell as being numeric. Converting cells which only contain a proper number does not help because if you enter a digit with a leading quote, Excel handles this as text but again this cannot be recognized as text in the csv file. The only solution is therefore to make an informed guess.  * Strictly speaking the delimiter is not defined in the specs. Some banks offer downloads with a semicolon used as separator instead of a comma. You might wonder why the name of this format is '''Comma''' Separated Values but anyway. Several versions of Excel do not recognize a semicolon as a separator.
Line 18: Line 20:
== Reading a CSV file == For details and background information see:
Line 20: Line 22:
 http://www.csvreader.com/csv_format.php

Note that the format comes with a nasty built-in-problem: there is no way to recognize a cell as being numeric. Converting cells which only contain a proper number does not help because if you enter a digit with a leading quote, Excel handles this as text but again this cannot be recognized as text in the csv file.

The only solution is therefore to make an informed guess. This informed guess can vary from file to file and person to person, so please look at the functions {{{Csv2MatrixWithDyalog}}} and {{{Csv2Numeric}}} below to see if you want to change them before you start using them.

== Reading and writing CSV files using APLX ==
Reading and writing CSV files in APLX is straightforward; You can just use the `⎕IMPORT` and `⎕EXPORT` system commands, specifying CSV as the format to use:

{{{
⍝ Reading a CSV file...
myVariable←⎕IMPORT 'C:\Users\simon\Desktop\spreadsheet_data.csv' 'csv'

⍝ Writing a CSV file
some_data←2 3⍴'APL' 'is' 'fine, very fine' 1 2.2 ¯3
some_data ⎕EXPORT 'C:\Users\simon\Desktop\new_data.csv' 'csv'
}}}
For another example of reading a CSV file of Google finance data and charting it, see [[CSVandChartingAplx| CSV and charting in APLX ]]

== Reading a CSV file using Dyalog APL ==
Line 22: Line 44:
attachment:cvsexcel3.jpg ||numeric|| char || date ||currency ||misc||
|| 1 ||1 || 2015-03-24 || 1.23||Yes ||
|| 2 ||Hello || 2015-01-01 || ¯10 || ||
|| 3 || || 1999-12-31 || ||No ||
|| 4 ||More || 2001-02-01 ||123456789.1 ||"Are your sure?" ||
|| ||Less || || || ||
|| 5 ||Much more || 2014-04-03 || 0 ||apples, carrots, and bananas||
Line 24: Line 52:
Saving this into a csv file, the file can be read into APL. The variable would look like this: Saving this into a csv file the file can be read into APL. The variable would look like this:
Line 26: Line 54:
attachment:csvapl.jpg {{attachment:csvapl.jpg}}
Line 30: Line 58:
 * partition the simple string from file  1. Partition the simple string from file
 1. Extract the data and build up the APL matrix
Line 32: Line 61:
 * extract the data and build up the APL matrix === First step: partition the string being read from file ===
With the following two functions this variable can be transformed into an APL array where every item represents a record. Data masked by double quotes (") remain unchanged.
Line 34: Line 64:
=== Step 1: Partition The String Being Read From File === The functions can deal with files from Unix, Mac and Windows.
Line 36: Line 66:
With the following two functions this variable can be transformed into an APL array where every item represents a record. Data masked by " remain unchanged. The functions can deal with files from Unix, Mac and Windows.

==== APL2 Version ====
Line 40: Line 67:
r←{ignoreBetween}PartitionRecordsWithAPL2 string;masked;cr;lf;bool r←PartitionRecordsWithDyalog string;masked;cr;lf;bool
Line 45: Line 72:
⍝ Note that everything between "ignoreBetween" is ignored.
⍝ This can be used to masked stuff between "" (CSV files), for example.
 ⎕IO←1
 (cr lf)←⎕TC[2 3] ⍝ <CarriageReturn> and <LineFeed>
 →L01×⍳2=⎕NC'ignoreBetween'
 ignoreBetween←'' ⍝ establish default
L01:
 →L02×⍳masked←0∊⍴ignoreBetween
 masked←~masked∨≠\masked←'"'=string ⍝ what is not escaped (between "")
L02:
 →L03×⍳~0∊bool←~(cr,lf)⍷masked/string ⍝ are there any unmasked cr/lf in "string"?
 bool←(~masked)∨masked\bool ⍝ "insert" the masked
 string[1+(~bool)/⍳⍴bool]←cr ⍝ convert lf into cr
 string←bool/string ⍝ remove original cr
 masked←bool/masked
 →L04
L03:→L03×⍳1∊bool←lf=masked/string ⍝ Are there any unmasked lf in "string"?
 (((~masked)∨masked\bool)/string)←cr ⍝ change them to cr
L04:r←(cr≠masked/string)⊂masked/string ⍝ use unmasked cr for partitioning
 ⎕IO←1 ⋄ ⎕ML←3
 (cr lf)←⎕UCS 10 13 ⍝ <CarriageReturn> and <LineFeed>
 :If 0<+/bool←(cr,lf)⍷string ⍝ are there any cr+lf in "string"?
     string←(~bool)/string ⍝ Let only the cr survive
 :EndIf
 :If 0<+/bool←cr=string ⍝ Are there still any cr's?
     (bool/string)←lf ⍝ Convert them to lf
 :EndIf
⍝ In the remaining string, there might be lf's inside text, Those
⍝ need to be masked before we decide where records really start.
 masked←~{⍵∨≠\⍵}'"'=string ⍝ what is not escaped (between "")
 :If 1∊bool←lf=masked/string ⍝ are there any unmasked lf in "string"?
     r←(~masked\bool)⊂string
 :Else ⍝ so it's a single record
     r←⊂string
 :EndIf
Line 66: Line 90:
==== Dyalog Version ==== === Second step: extract the real data ===
Line 68: Line 92:
r←{ignoreBetween}PartitionRecordsWithDyalog string;masked;cr;lf;bool
⍝ Takes a string and partitions records.
⍝ Can deal with Mac/Unix/Windows files.
⍝ For that, CR+LF as well as single LFs are converted into CR.
⍝ CR is then used to partition "string".
⍝ Note that everything between "ignoreBetween" is ignored.
⍝ This can be used to masked stuff between "" (CSV files), for example.
 r←{sep}Csv2MatrixWithDyalog csv;bool;⎕IO
⍝ Convert vector-of-text-vectors "csv" that is assumed to
⍝ come from a *.csv file and which got already partitioned
⍝ into an APL matrix. Takes care of escaped stuff etc.
⍝ "sep" defaults to a comma.
Line 76: Line 98:
 (cr lf)←⎕TC[2 3] ⍝ <CarriageReturn> and <LineFeed>
 :If 0=⎕NC'ignoreBetween'
     ignoreBetween←'' ⍝ establish default
 :EndIf
 :If ~masked←0∊⍴ignoreBetween
     masked←~{⍵∨≠\⍵}'"'=string ⍝ what is not escaped (between "")
 :EndIf
 :If 0∊bool←~(cr,lf)⍷masked/string ⍝ are there any unmasked cr/lf in "string"?
     bool←(~masked)∨masked\bool ⍝ "insert" the masked
     string[1+{⍵/⍳⍴⍵}~bool]←cr ⍝ convert lf into cr
     string←bool/string ⍝ remove original cr
     masked←bool/masked
 :ElseIf 1∊bool←lf=masked/string ⍝ Are there any unmasked lf in "string"?
     (((~masked)∨masked\bool)/string)←cr ⍝ change them to cr
 :EndIf
 r←(cr≠masked/string)⊂masked/string
 sep←{2=⎕NC ⍵:⍎⍵ ⋄ ','}'sep'
 r←(⌽∨\0≠⌽↑∘⍴¨csv)/csv ⍝ remove empty stuff from the end if any
 r←',',¨r ⍝ Add starting seperator
 bool←{~{⍵∨≠\⍵}'"'=⍵}¨r ⍝ prepare booleans useful to mask escaped stuff
 r←r{⎕ML←1 ⋄ ⍺⊂⍨⍵=sep}¨bool{⍺\⍺/⍵}¨r ⍝ partition fields by unmasked commas
 r←⊃{1↓¨⍵}¨r ⍝ Drop command and transform to a matrix
 r←{'"'≠1⍴⍵:⍵ ⋄ ¯1↓1↓⍵}¨r ⍝ remove leading and trailing "
 r←Csv2Numeric r ⍝ Convert numeric cells
 r←(~'""'∘⍷¨r)/¨r ⍝ Reduce double-" to single ones
Line 94: Line 109:
=== Step 2: Extract The Real Data ===

==== APL2 Version ====
Line 98: Line 110:
r←Csv2MatrixWithAPL2 csv;buffer;⎕IO;isNotEmpty;mask;bool
⍝ Convert vector-of-text-vectors "csv" that is assumed to
⍝ come from a *.csv file and which got already partinioned
⍝ into an APL matrix. Takes care of escaped stuff.
 ⎕IO←1
 r←(⌽∨\0≠⌽↑¨⍴¨r)/r ⍝ remove empty stuff from the end if any
 mask←~mask∨¨≠\¨mask←'"'=¨r ⍝ what is not escaped (between "")
 r←⊃(','≠¨mask\¨mask/¨r)⊂¨r ⍝ partition fields by commas
 r←('"'=¨↑¨r)↓¨r ⍝ remove leading "
 r←(-'"'=¨↑¨¯1↑¨r)↓¨r ⍝ remove trailing "
 isNotEmpty←0<↑¨⍴¨r ⍝ remember empty fields
 bool←,isNotEmpty∧∧/¨r∊¨⊂'0123456789.' ⍝ fields which contains only ...
 (bool/,r)←⍎¨bool/,r ⍝ Make those numeric
 r←{ignore}Csv2Numeric r;buffer
⍝ Transform cells that contain digits into numeric values, BUT:
⍝ * Commas are ignored.
⍝ * "$£€¥" are ignored because the left argument "ignore" defaults to those.
⍝ * Blanks are removed
⍝ Example:
⍝ (¯10 3 4 1234.5 12 1000 '1A')←Csv2Numeric '-10' '3' '4' '123,4.5' '£12' '1E3' '1A'
 ignore←{0<⎕NC ⍵:⍎⍵ ⋄ '$£€¥'}'ignore'
 buffer←{0=+/bool←'-'=w←⍵:⍵ ⋄ (bool/w)←'¯' ⋄ w}¨r ⍝ "buffer" is a copy of r with "¯" for "-"
 r←buffer{(0∊⍴⍵):'' ⋄ ,↑1⊃v←⎕VFI ⍺~' ,',ignore:↑2⊃v ⋄ ⍵}¨r ⍝ make fields with appropriate content numeric
Line 113: Line 122:
==== Dyalog Version ==== === Putting it all together ===
Line 116: Line 125:
r←Csv2MatrixWithDyalog csv;bool;⎕IO
⍝ Convert vector-of-text-vectors "csv" that is assumed to
⍝ come from a *.csv file and which got already partinioned
⍝ into an APL matrix. Takes care of escaped stuff.
 ⎕IO←1 ⋄ ⎕ML←3
 r/⍨←⌽∨\0≠⌽↑∘⍴¨r ⍝ remove empty stuff from the end if any
 bool←{~{⍵∨≠\⍵}'"'=⍵}¨r ⍝ prepare booleans useful to mask escaped stuff
 r←⊃r{⍺⊂⍨⍵≠','}¨bool{⍺\⍺/⍵}¨r ⍝ partition fields by unmasked commas
 r←{'"'≠1⍴⍵:⍵ ⋄ ¯1↓1↓⍵}¨r ⍝ remove leading and trailing "
 r←{↑1⊃v←⎕VFI ⍵:↑2⊃v ⋄ ⍵}¨r ⍝ make fields whith appropriate content numeric scalars
 r←{sep} DealWithCsv filename;data
⍝ Read "filename" which is assumed to be a *.csv file
⍝ and convert it into a matrix
 sep←{2=⎕NC ⍵:⍎⍵ ⋄ ','}'sep'
 data←FileRead filename
 data←PartitionRecordsWithDyalog data
 r←sep Csv2MatrixWithDyalog data
Line 127: Line 133:

== The final step ==

Put it all together (here for the Dyalog version):
{{{
 r←DealWithCsv filename;data
⍝ Read "filename" which is assumed to be a *.csv file
⍝ and converts it into a matrix
 data←FileRead filename
 data←'"'PartitionRecordsWithDyalog data
 r←Csv2MatrixWithDyalog data
}}}
Line 142: Line 135:
attachment:csvinapl.jpg {{attachment:csvinapl.jpg}}
Line 146: Line 139:
== Writing a CSV file ==
== Writing a CSV file using Dyalog APL ==
Line 152: Line 144:
 APL is fine, very fine 
   1 2.2 ¯3 
 APL is fine, very fine
   1 2.2 ¯3
Line 155: Line 147:

The following functions take such an array as right argument and convert it into a string that can be written to a file with the extension ".csv". The left argument defaults to "windows" and can be "unix" or "mac" as well. Note that the left argument is case sensitive. The left argument is used to determine the appropriate record separator.

=== APL2 Version ===

(Volunteers needed)

=== Dyalog Version ===
The following function takes such an array as right argument and converts it into a string that can be written to a file with the extension ".csv". The left argument defaults to "windows" and can be "unix" or "mac" as well. Note that the left argument is case sensitive. The left argument is used to determine the appropriate record separator.
Line 165: Line 150:
 r←{os}Array2CsvWithDyalog array;cr;lf;sep;bool;IsChar  r←{os}Array2CsvWithDyalog array;cr;lf;sep;bool;IsChar;dq
Line 173: Line 158:
 (cr lf)←⎕TC[2 3] ⍝ <CarriageReturn> and <LineFeed>
 sep←('windows' 'unix' 'mac'⍳⊂os)⊃(cr,lf)lf cr ⍝ select proper record separator
 IsChar←{0 2∊⍨10|⎕DR ⍵} ⍝ Version 12 compatible
 bool←,~IsChar¨array ⍝ locate number
 (bool/,array)←⍕¨bool/,array                     ⍝ make numbers text
 bool←
,(lf∊¨array)∨','∊¨array   ⍝ where are special chars used?
 (bool/,array)←{'"',⍵,'"'}¨bool/,array ⍝ escape field with special chars
 array←{⊃{⍺,',',⍵}/⍵}¨↓array ⍝ separate fields by comma
 r←⊃,/array,¨⊂sep ⍝ make it simpel
 ((r='¯')/r)←'-' ⍝ Handle ¯
 (cr lf)←⎕TC[2 3]  ⍝ <CarriageReturn> and <LineFeed>
 sep←('windows' 'unix' 'mac'⍳⊂os)⊃(cr,lf)lf cr  ⍝ select proper record separator
 IsChar←{0 2∊⍨10|⎕DR ⍵}  ⍝ Version 12 compatible
 bool←,~IsChar¨array  ⍝ locate numbers
 (bool/,array)←{('-',⍵)[('¯',⍵)⍳⍵]}¨⍕¨bool/,array ⍝ make numbers text and convert ¯ to -
 dq←,'"'∊¨array ⍝ Where are double quotes in the text?
 (dq/,array)←{⍵/⍨1+'"'=⍵}¨dq/,array ⍝ Double the double quotes
 bool←dq∨
,(lf∊¨array)∨','∊¨array ⍝ where are special chars used?
 (bool/,array)←{'"',⍵,'"'}¨bool/,array  ⍝ escape field with special chars
 array←{⊃{⍺,',',⍵}/⍵}¨↓array  ⍝ separate fields by comma
 r←⊃,/array,¨⊂sep  ⍝ make it simple
Line 184: Line 170:
Line 187: Line 172:
   #.Array2Csv 2 3⍴'APL' 'is' 'fine, very fine' 1 2.2 ¯3
APL,is,"fine, very fine"             
1,2.2,-3                             
#.Array2CsvWithDyalog 2 3⍴'APL' 'really "really" is' 'fine, very fine' 1 2.2 ¯3
APL,"really ""really"" is","fine, very fine"
1,2.2,-3
Line 195: Line 178:

|| Update -- KaiJaeger <<DateTime(2012-08-05T11:06:46Z)>> incorporating a couple of findings/suggestions from EllisMorgan.||
|| Update -- KaiJaeger <<DateTime(2015-03-24T11:26:39Z)>> bug fix: empty cells were not handled correctly.||
|| Update -- KaiJaeger <<DateTime(2016-02-02T13:28:21Z)>> Improvements as suggested by PierreGilbert. ||
----
CategoryArticles

CSV to APL

CSV stands for comma separated values. Those files are still used to transport tabular data between applications that are not directly connected.

Such files can be edited with any spreadsheet application like Microsoft Excel.

There are some things one need to know about CSV files in order to deal with them:

  • Fields are separated by commas. Well, mostly.
  • Records are separated with system end of line characters, CRLF (ASCII 13 Dec or 0D Hex and ASCII 10 Dec or 0A Hex respectively) for Windows, LF for Unix, and CR for Mac
  • If a field contains either a comma or a double quote or one of the end line chars either the char(s) or the whole contents needs to be escaped.

Excel escapes these values by embedding the field inside a set of double quotes. For example, a single cell with the text apples, carrots, and oranges becomes "apples, carrots, and oranges".

  • Strictly speaking the delimiter is not defined in the specs. Some banks offer downloads with a semicolon used as separator instead of a comma. You might wonder why the name of this format is Comma Separated Values but anyway. Several versions of Excel do not recognize a semicolon as a separator.

For details and background information see:

Note that the format comes with a nasty built-in-problem: there is no way to recognize a cell as being numeric. Converting cells which only contain a proper number does not help because if you enter a digit with a leading quote, Excel handles this as text but again this cannot be recognized as text in the csv file.

The only solution is therefore to make an informed guess. This informed guess can vary from file to file and person to person, so please look at the functions Csv2MatrixWithDyalog and Csv2Numeric below to see if you want to change them before you start using them.

Reading and writing CSV files using APLX

Reading and writing CSV files in APLX is straightforward; You can just use the ⎕IMPORT and ⎕EXPORT system commands, specifying CSV as the format to use:

⍝ Reading a CSV file...
myVariable←⎕IMPORT 'C:\Users\simon\Desktop\spreadsheet_data.csv' 'csv'

⍝ Writing a CSV file
some_data←2 3⍴'APL' 'is' 'fine, very fine' 1 2.2 ¯3
some_data ⎕EXPORT 'C:\Users\simon\Desktop\new_data.csv' 'csv'

For another example of reading a CSV file of Google finance data and charting it, see CSV and charting in APLX

Reading a CSV file using Dyalog APL

Given an Excel spreadsheet that looks like this:

numeric

char

date

currency

misc

1

1

2015-03-24

1.23

Yes

2

Hello

2015-01-01

¯10

3

1999-12-31

No

4

More

2001-02-01

123456789.1

"Are your sure?"

Less

5

Much more

2014-04-03

0

apples, carrots, and bananas

Saving this into a csv file the file can be read into APL. The variable would look like this:

csvapl.jpg

To convert this into an APL matrix is a two-step-process:

  1. Partition the simple string from file
  2. Extract the data and build up the APL matrix

First step: partition the string being read from file

With the following two functions this variable can be transformed into an APL array where every item represents a record. Data masked by double quotes (") remain unchanged.

The functions can deal with files from Unix, Mac and Windows.

r←PartitionRecordsWithDyalog string;masked;cr;lf;bool
⍝ Takes a string and partitions records.
⍝ Can deal with Mac/Unix/Windows files.
⍝ For that, CR+LF as well as single LFs are converted into CR.
⍝ CR is then used to partition "string".
 ⎕IO←1 ⋄ ⎕ML←3
 (cr lf)←⎕UCS 10 13                         ⍝ <CarriageReturn> and <LineFeed>
 :If 0<+/bool←(cr,lf)⍷string                ⍝ are there any cr+lf in "string"?
     string←(~bool)/string                  ⍝ Let only the cr survive
 :EndIf
 :If 0<+/bool←cr=string                     ⍝ Are there still any cr's?
     (bool/string)←lf                       ⍝ Convert them to lf
 :EndIf
⍝ In the remaining string, there might be lf's inside text, Those
⍝ need to be masked before we decide where records really start.
 masked←~{⍵∨≠\⍵}'"'=string                  ⍝ what is not escaped (between "")
 :If 1∊bool←lf=masked/string                ⍝ are there any unmasked lf in "string"?
     r←(~masked\bool)⊂string
 :Else ⍝ so it's a single record
     r←⊂string
 :EndIf

Second step: extract the real data

 r←{sep}Csv2MatrixWithDyalog csv;bool;⎕IO
⍝ Convert vector-of-text-vectors "csv" that is assumed to
⍝ come from  a *.csv file and which got already partitioned
⍝ into an APL matrix. Takes care of escaped stuff etc.
⍝ "sep" defaults to a comma.
 ⎕IO←1 ⋄ ⎕ML←3
 sep←{2=⎕NC ⍵:⍎⍵ ⋄ ','}'sep'
 r←(⌽∨\0≠⌽↑∘⍴¨csv)/csv          ⍝ remove empty stuff from the end if any
 r←',',¨r                       ⍝ Add starting seperator
 bool←{~{⍵∨≠\⍵}'"'=⍵}¨r         ⍝ prepare booleans useful to mask escaped stuff
 r←r{⎕ML←1 ⋄ ⍺⊂⍨⍵=sep}¨bool{⍺\⍺/⍵}¨r   ⍝ partition fields by unmasked commas
 r←⊃{1↓¨⍵}¨r                    ⍝ Drop command and transform to a matrix
 r←{'"'≠1⍴⍵:⍵ ⋄ ¯1↓1↓⍵}¨r       ⍝ remove leading and trailing "
 r←Csv2Numeric r                ⍝ Convert numeric cells
 r←(~'""'∘⍷¨r)/¨r               ⍝ Reduce double-" to single ones

 r←{ignore}Csv2Numeric r;buffer
⍝ Transform cells that contain digits into numeric values, BUT:
⍝ * Commas are ignored.
⍝ * "$£€¥" are ignored because the left argument "ignore" defaults to those.
⍝ * Blanks are removed
⍝ Example:
⍝ (¯10 3 4 1234.5 12 1000  '1A')←Csv2Numeric '-10' '3' '4' '123,4.5' '£12' '1E3' '1A'
 ignore←{0<⎕NC ⍵:⍎⍵ ⋄ '$£€¥'}'ignore'
 buffer←{0=+/bool←'-'=w←⍵:⍵ ⋄ (bool/w)←'¯' ⋄ w}¨r  ⍝ "buffer" is a copy of r with "¯" for "-"
 r←buffer{(0∊⍴⍵):'' ⋄ ,↑1⊃v←⎕VFI ⍺~' ,',ignore:↑2⊃v ⋄ ⍵}¨r ⍝ make fields with appropriate content numeric

Putting it all together

 r←{sep} DealWithCsv filename;data
⍝ Read "filename" which is assumed to be a *.csv file
⍝ and convert it into a matrix
 sep←{2=⎕NC ⍵:⍎⍵ ⋄ ','}'sep'
 data←FileRead filename
 data←PartitionRecordsWithDyalog data
 r←sep Csv2MatrixWithDyalog data

The resulting variable in APL would look like this:

csvinapl.jpg

Note that the 1 in the second row/second column got converted into the number because the contents of the cell remained of digits only. However, in the original Excel spreadsheet that cell is text; this is indicated by the small green triangle. This information is not contained in the CSV file.

Writing a CSV file using Dyalog APL

Given an APL array like:

   ⎕←2 3⍴'APL' 'is' 'fine, very fine' 1 2.2 ¯3
 APL   is  fine, very fine
   1  2.2    ¯3

The following function takes such an array as right argument and converts it into a string that can be written to a file with the extension ".csv". The left argument defaults to "windows" and can be "unix" or "mac" as well. Note that the left argument is case sensitive. The left argument is used to determine the appropriate record separator.

 r←{os}Array2CsvWithDyalog array;cr;lf;sep;bool;IsChar;dq
 ⎕IO←1 ⋄ ⎕ML←3
 :If 0=⎕NC'os'
     os←'windows'
 :EndIf
 'Invalid left argument; must be one of: windows, unix, mac'⎕SIGNAL 11/⍨~(⊂os)∊'windows' 'unix' 'mac'
 'Right argument must have a depth of 2'⎕SIGNAL 11/⍨2≠≡array
 'Right argument must be either a matrix or a vector'⎕SIGNAL 11/⍨~(⍴⍴array)∊1 2
 (cr lf)←⎕TC[2 3]                                 ⍝ <CarriageReturn> and <LineFeed>
 sep←('windows' 'unix' 'mac'⍳⊂os)⊃(cr,lf)lf cr    ⍝ select proper record separator
 IsChar←{0 2∊⍨10|⎕DR ⍵}                           ⍝ Version 12 compatible
 bool←,~IsChar¨array                              ⍝ locate numbers
 (bool/,array)←{('-',⍵)[('¯',⍵)⍳⍵]}¨⍕¨bool/,array ⍝ make numbers text and convert ¯ to -
 dq←,'"'∊¨array                                   ⍝ Where are double quotes in the text?
 (dq/,array)←{⍵/⍨1+'"'=⍵}¨dq/,array               ⍝ Double the double quotes
 bool←dq∨,(lf∊¨array)∨','∊¨array                  ⍝ where are special chars used?
 (bool/,array)←{'"',⍵,'"'}¨bool/,array            ⍝ escape field with special chars
 array←{⊃{⍺,',',⍵}/⍵}¨↓array                      ⍝ separate fields by comma
 r←⊃,/array,¨⊂sep                                 ⍝ make it simple

⍝ Example:
#.Array2CsvWithDyalog 2 3⍴'APL' 'really "really" is' 'fine, very fine' 1 2.2 ¯3
APL,"really ""really"" is","fine, very fine"
1,2.2,-3

Author: KaiJaeger

Update -- KaiJaeger 2012-08-05 11:06:46 incorporating a couple of findings/suggestions from EllisMorgan.

Update -- KaiJaeger 2015-03-24 11:26:39 bug fix: empty cells were not handled correctly.

Update -- KaiJaeger 2016-02-02 13:28:21 Improvements as suggested by PierreGilbert.


CategoryArticles

CsvToApl (last edited 2017-02-16 19:12:54 by KaiJaeger)