Sign in Join
Wiki copytemporary

CSV: what changed

Revision 3 to 4. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11## Overview
22
33CSV means Comma-Separated Values, and is a popular file format that is unfortunately not completely standardized.
44
55It is a text based file format with
66
77- data fields separated by commas (or, in variants, other characters like tabs and semicolons)
88- there may or not be a header line that lists the field names
99- field data containing delimiters may be prohibited or enclosed by quotes (most commonly the double quote character)
1010- line endings (#13 and/or \#10) may or may not be allowed in field data
1111
1212[RFC4180](http://www.rfc-editor.org/rfc/rfc4180.txt) tries to codify and standardize existing practice; it makes sense to conform to this standard when writing CSV data (and accept all RFC4180 data when reading).
1313
1414A different, Creativyst/Excel, CSV pseudo-format specification can be found [here](http://www.creativyst.com/Doc/Articles/CSV/CSV01.htm#FileFormat).
1515
1616A sample CSV snippet:
1717
18-```
18+```text
1919FirstName,Surname,DOB,Remarks
2020Jim,Weston,19560818,"Also known as ""The Butcher"""
2121Alice,Cooper,19760312,""
2222```
2323
2424Apparently a header line is used here, as is quoting using double quotes.
2525
2626Spreadsheet packages such as Microsoft Excel and OpenOffice/LibreOffice Calc are able to export to and import from this format. However, as Microsoft Excel may interpret some fields such as date fields differently depending on a user's OS locale, it may pay to find alternative ways of transferring data (e.g. using the [[FPSpreadsheet]] code).
2727
2828## CSV and SDF
2929
3030Delphi (and Free Pascal) have a very similar (but not identical) format, SDF. See [[SDF]] for more details.
3131
3232## Implementations
3333
3434### CsvDocument
3535
3636[[CsvDocument]] is a robust implementation of both the RFC 4180 CSV format and the alternative Creativyst/Excel CSV format. It offers both line-based and document-based access. Recommended for use with FPC/Lazarus. See [[CsvDocument]].
3737
3838### DelimitedText
3939
4040TStringList offers the DelimitedText property. This parses a line of text out into separate fields. Note, however, that DelimitedText is supposed to be in SDF, a Delphi-specific format that is very much like CSV, but does not conform with RFC4180 completely.
4141
4242Tips: when reading CSV data, set the *StrictDelimiter* property to true.
4343
4444When writing out CSV data, set *StrictDelimiter* to false and output the *DelimitedText* property. One oddity is that e.g. tab characters are removed when writing out data using *StrictDelimiter:=false*
4545
4646#### SDF format
4747
4848See [[SDF|SDF format]]
4949
5050### SDFDataset
5151
5252Free Pascal offers the [[TSdfDataSet]], which stores data in SDF format. Note: FPC 2.6.x and earlier store sdfdataset data in a format that is not completely CSV or completely SDF compatible.
5353
5454As indicated, [[SDF]] differs from [[CSV]]. Depending on the flavour of CSV, this format may be close enough to what a reading application expects to function. If you need an RFC4180 compliant CSV format, use [[TCSVDataSet]] instead.
5555
5656#### TSdfDataset and TFixedDataset sample files
5757
5858Sample SDF file
5959
6060
6161Below is a sample database for TSdfDataset. Note that the first line has the names of the fields and that we are using commas as separators:
6262
63-```
63+```text
6464ID,NAMEEN,NAMEPT,HEIGHT,WIDTH,PINS,DRAWINGCODE
65651,resistor,resistor,1,1,1,LINE
66662,capacitor,capacitor,1,1,1,LINE
67673,transistor npn,transistor npn
6868```
6969
7070- Sample TFixedDataset file
7171
7272
7373Each record occupies a fixed amount of space, and if the field is smaller, spaces should be used to fill the remaining size.
7474
75-```
75+```text
7676Name = 15 chars; Surname = 15 chars; e_mail = 20 chars;
7777```
7878
7979`Piet           Pompies                  piet@pompies.net`
8080
8181#### Using the datasets: example code
8282
8383Sometimes it is useful to create the dataset and work with it completely in code, and the following code will do exactly this. Note some peculiarities of TSdfDataset/TFixedDataset:
8484
8585- The lines in the database can have a maximum size of about 300. A fix is being researched.
8686- It is necessary to add the field definitions. Some datasets are able to fill this information alone from the database file.
8787- One should set FirstLineAsSchema to true, to indicate that the first line includes the field names and positions.
8888- The Delimiter property holds the separator for the fields. It will not be possible to use this char in strings in the database. Similarly it will currently not be possible to have line endings in the database because they mark the change between records. It's possible to overcome this by substituting the needed comma or line ending with another rarely used char, like \#. When showing the data on screen all \# chars could be converted to line endings and the inverse when storing data back to the database. The ReplaceString routine is useful here.
8989
9090``` pascal
9191uses sdfdata, db;
9292
9393constructor TComponentsDatabase.Create;
9494var
9595 FDataset: TSdfDataset;
9696begin
9797 inherited Create;
9898
9999 FDataset := TSdfDataset.Create(nil);
100100 FDataset.FileName := vConfigurations.ComponentsDBFile;
101101 FDataset.FileMustExist := false; //don't require existing csv file
102102
103103 // Not necessary with TSdfDataset:
104104 // FDataset.TableName := STR_DB_COMPONENTS_TABLE;
105105 // FDataset.PrimaryKey := STR_DB_COMPONENTS_ID;
106106
107107 // Adds field definitions
108108 FDataset.FieldDefs.Add('ID', ftString);
109109 //schema addition is needed so that sdfdataset writes out the csv header correctly:
110110 FDataset.Schema.Add('ID');
111111 FDataset.FieldDefs.Add('NAMEEN', ftString);
112112 FDataset.Schema.Add('NAMEEN');
113113 FDataset.FieldDefs.Add('NAMEPT', ftString);
114114 FDataset.Schema.Add('NAMEPT');
115115 FDataset.FieldDefs.Add('HEIGHT', ftString);
116116 FDataset.Schema.Add('HEIGHT');
117117 FDataset.FieldDefs.Add('WIDTH', ftString);
118118 FDataset.Schema.Add('WIDTH');
119119 FDataset.FieldDefs.Add('PINS', ftString);
120120 FDataset.Schema.Add('PINS');
121121 FDataset.FieldDefs.Add('DRAWINGCODE', ftString);
122122 FDataset.Schema.Add('DRAWINGCODE');
123123
124124 // Necessary for TSdfDataset
125125 FDataset.Delimiter := ',';
126126 FDataset.FirstLineAsSchema := True;
127127
128128 FDataset.Active := True;
129129
130130 // Sets the initial record
131131 CurrentRecNo := 1;
132132 FDataset.First;
133133end;
134134```
135135
136136When using [[TSdfDataSet]] directly be aware that RecNo, although it is implemented, does not work as a way to move through the dataset whether reading or writing records. The standard navigation routines like First, Next, Prior and Last work as expected, so you need to use them rather than RecNo.
137137
138138If you are used to using absolute record numbers to navigate around a database you can implement your own version of RecNo. Declare a global longint variable called CurrentRecNo which will hold the current RecNo value. Remember that this variable will have the same convention as RecNo, so the first record has number 1 (it is not zero-based). After activating the database initialize the database to the first record with TSdfDataset.First and set CurrentRecNo := 1.
139139
140140Example code:
141141
142142``` pascal
143143{@@
144144 Moves to the desired record using TDataset.Next and TDataset.Prior
145145 This avoids using TDataset.RecNo which doesn't navigate reliably in any dataset.
146146
147147 @param AID Indicates the record number. The first record has number 1
148148}
149149procedure TComponentsDatabase.GoToRec(AID: Integer);
150150begin
151151 // We are before the desired record, move forward
152152 if CurrentRecNo < AID then
153153 begin
154154 while (not FDataset.EOF) and (CurrentRecNo < AID) do
155155 begin
156156 FDataset.Next;
157157 FDataset.CursorPosChanged;
158158 Inc(CurrentRecNo);
159159 end;
160160 end
161161 // We are after the desired record, move back
162162 else if CurrentRecNo > AID then
163163 begin
164164 while (CurrentRecNo >= 1) and (CurrentRecNo > AID) do
165165 begin
166166 FDataset.Prior;
167167 FDataset.CursorPosChanged;
168168 Dec(CurrentRecNo);
169169 end;
170170 end;
171171end;
172172```
173173
174174### Data Export
175175
176176Free Pascal/Lazarus database export functionality (e.g. TCSVExporter on the Data Export tab - requires installation of the [[lazdbexport]] package) offers CSV export functionality for [[TDataSet|datasets]].
177177
178178### Jan's CSV components
179179
180180See [[JCSV (Jans CSV Components)]].
181181
182182### ZMSQL
183183
184184[[ZMSQL]] stores data in semicolon-delimited files (using SDF?).
185185
186186---
187187*From the Free Pascal wiki, page "CSV", as it stood on 2020-07-18 after a change by FTurtle, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history