aboutsummaryrefslogtreecommitdiff
path: root/readme.md
diff options
context:
space:
mode:
Diffstat (limited to 'readme.md')
-rw-r--r--readme.md69
1 files changed, 41 insertions, 28 deletions
diff --git a/readme.md b/readme.md
index 2b080cf..9de8941 100644
--- a/readme.md
+++ b/readme.md
@@ -41,12 +41,13 @@ for ( size_t nf; (nf = csv_read(&csv, in)) != 0; ) { /* eine Zeile aus `in` les
41~~~ 41~~~
42 42
43Es sind nur zwei Funktionen notwendig, um die Daten des FILE-Streams `in` vollständig zu verarbeiten: 43Es sind nur zwei Funktionen notwendig, um die Daten des FILE-Streams `in` vollständig zu verarbeiten:
44`csv_read()` liest eine Zeile aus dem Parameter `in` und liefert die Anzahl der erfolgreich geparsten Felder oder **0** bei Dateiende. 44`csv_read()` liest eine Zeile aus dem Stream `in` und liefert die Anzahl der erfolgreich analysierten Felder oder **0** bei Dateiende.
45`csv_field()` liefert einen (read only) `const char`-Zeiger für das i-te Feld. 45`csv_field()` liefert einen (read only) `const char`-Zeiger für das *i*te Feld.
46 46
47Der Speicher für das CSV-Objekt wird automatisch von `csv_read()` beim ersten Aufruf alloziert und wieder freigeben nachdem der Stream `in` vollständig gelesen wurde. 47Der Speicher für das CSV-Objekt wird automatisch von `csv_read()` beim ersten Aufruf alloziert und wieder freigeben nachdem der Stream `in` vollständig gelesen wurde.
48 48
49Mehr Arbeit ist nur noch dann notwendig, wenn eine Fehlerbehandlung zum Projekt hinzugefügt wird. Für die meisten einfachen Fälle reichen diese Schritte wahrscheinlich schon aus. 49Mehr Arbeit ist nur noch dann notwendig, wenn eine Fehlerbehandlung zum Projekt hinzugefügt wird.
50Für die meisten einfachen Fälle reichen diese Schritte wahrscheinlich schon aus.
50 51
51## Motivation 52## Motivation
52 53
@@ -58,7 +59,7 @@ Die meisten anderen CSV-Bibliotheken passen nicht:
58- fehlerhafte Verarbeitung von "problematischen" CSV-Dateien (Zeilenumbrüche innerhalb einer Zelle) 59- fehlerhafte Verarbeitung von "problematischen" CSV-Dateien (Zeilenumbrüche innerhalb einer Zelle)
59- unvorteilhafte Lizenz (GPL) 60- unvorteilhafte Lizenz (GPL)
60- umständliche API (Callbacks zum Verarbeiten der Inhalte, seltsames Handling am Dateiende) 61- umständliche API (Callbacks zum Verarbeiten der Inhalte, seltsames Handling am Dateiende)
61- globale Variablen, die den Einsatz in Multi-Threadumgebungen unmöglich machen 62- globale Variablen, die den Einsatz in Multithread-Umgebungen unmöglich machen
62- C++ wenn ich C brauche 63- C++ wenn ich C brauche
63- Java/Python/Rust/Ruby/${YourLanguageHere} wenn ich C brauche 64- Java/Python/Rust/Ruby/${YourLanguageHere} wenn ich C brauche
64- Langsam, Aufgebläht 65- Langsam, Aufgebläht
@@ -105,7 +106,9 @@ csv_cleanup(&csv);
105 106
106Dieser Code erzeugt ein neues `csv_t`-Objekt im Speicher welches anschließend durch `csv_init()` mit Standardwerten initialisiert wird. 107Dieser Code erzeugt ein neues `csv_t`-Objekt im Speicher welches anschließend durch `csv_init()` mit Standardwerten initialisiert wird.
107 108
108Dann wird der Datenstrom `in` zeilenweise in den Speicher eingelesen und innerhalb der Schleife verarbeitet. Die Anzahl der Felder pro Zeile wird mit Hilfe der Funktion `csv_nfields()` ermittelt. Alternativ liefert auch der Returnwert von `csv_read()` die Feldanzahl: 109Dann wird der Datenstrom `in` zeilenweise in den Speicher eingelesen und innerhalb der Schleife verarbeitet.
110Die Anzahl der Felder pro Zeile wird mit Hilfe der Funktion `csv_nfields()` ermittelt.
111Alternativ liefert auch der Rückgabewert von `csv_read()` die Feldanzahl:
109 112
110~~~c 113~~~c
111// ... 114// ...
@@ -120,13 +123,16 @@ while ( (nf = csv_read(&csv, in)) != 0 ) {
120Der Rückgabewert **0** bei einem Aufruf von `csv_read()` kennzeichnet das Ende des Datenstroms. 123Der Rückgabewert **0** bei einem Aufruf von `csv_read()` kennzeichnet das Ende des Datenstroms.
121 124
122> **Hinweis:** 125> **Hinweis:**
123> Das Ende einer CSV-Datei mit 0-gelesenen Feldern zu kennzeichnen ist vollkommen legitim. Eine "leere" Zeile innerhalb der Datei würde nämlich als genau ein (leeres) Feld interpretiert, so dass der Rückgabewert hier **1** wäre. Anders gesagt: Es gibt in CSV-Dateien keine Zeilen mit 0 Feldern. 126> Das Ende einer CSV-Datei mit 0-gelesenen Feldern zu kennzeichnen ist vollkommen legitim.
127> Eine "leere" Zeile innerhalb der Datei würde nämlich als genau ein (leeres) Feld interpretiert, so dass der Rückgabewert hier **1** wäre.
128> Anders gesagt: Es gibt in CSV-Dateien keine Zeilen mit 0 Feldern.
124 129
125Der Aufruf von `csv_cleanup()` ist nicht zwingend notwendig, da alle belegten Ressourcen beim letzten `csv_read()` automatisch freigegeben werden, wenn es keinen Fehler bei der Dateiverarbeitung gab. 130Der Aufruf von `csv_cleanup()` ist nicht zwingend notwendig, da alle belegten Ressourcen beim letzten `csv_read()` automatisch freigegeben werden, wenn es keinen Fehler bei der Dateiverarbeitung gab.
126 131
127**2\. Optionen einstellen** 132**2\. Optionen einstellen**
128 133
129Wenn nichts weiter angegeben ist, gehen wir von einem Komma (`,`) als Feldtrenner sowie doppelten Anführungszeichen (`"`) als Feldbegrenzer aus. Die meisten CSV-Dateien im deutschsprachigen Raum verwenden jedoch ein Semikolon (`;`) zum Trennen der einzelnen Felder. 134Wenn nichts weiter angegeben ist, gehen wir von einem Komma (`,`) als Feldtrenner sowie doppelten Anführungszeichen (`"`) als Feldbegrenzer aus.
135Die meisten CSV-Dateien im deutschsprachigen Raum verwenden jedoch ein Semikolon (`;`) zum Trennen der einzelnen Felder.
130 136
131Wir berücksichtigen dies bei der Initialisierung eines `csv_options_t`-Objektes. 137Wir berücksichtigen dies bei der Initialisierung eines `csv_options_t`-Objektes.
132 138
@@ -139,21 +145,24 @@ csv_t csv;
139csv_init_opt(&csv, &csv_options); 145csv_init_opt(&csv, &csv_options);
140~~~ 146~~~
141 147
142Damit wir nicht alle Felder in `csv_options` ändern müssen, initialisieren wir dieses zunächst mit `csv_default_options` und passen dann `field_delimiter` und `field_separator` für unsere Bedürfnisse an. Das `csv`-Objekt wird nun mit der Funktion `csv_init_opt()` initialisiert, die einen zusätzlichen Zeiger auf ein `csv_options_t`-Objekt erwartet. 148Damit wir nicht alle Felder in `csv_options` ändern müssen, initialisieren wir dieses zunächst mit den Standardwerten aus `csv_default_options` und passen dann `field_delimiter` und `field_separator` für unsere Bedürfnisse an.
149Das `csv`-Objekt wird nun mit der Funktion `csv_init_opt()` initialisiert, die einen zusätzlichen Zeiger auf ein `csv_options_t`-Objekt erwartet.
143 150
144> **Hinweis:** 151> **Hinweis:**
145> `csv_init(&csv);` ist letztlich nur eine kürzere Schreibweise für `csv_init_opt(&csv, &csv_default_options);`. 152> `csv_init(&csv);` ist letztlich nur eine kürzere Schreibweise für `csv_init_opt(&csv, &csv_default_options);`.
146 153
147> **Hinweis:** 154> **Hinweis:**
148> Die Standardwerte für Feldtrenner und Feldbegrenzer lassen sich als Übersetzungsoptionen beim Compilieren über die Makros `CSV_DEFAULT_SEPARATOR` und `CSV_DEFAULT_DELIMITER` setzen. 155> Die Standardwerte für Feldtrenner und Feldbegrenzer lassen sich als Übersetzungsoptionen beim Compilieren über die Makros `CSV_DEFAULT_SEPARATOR` und `CSV_DEFAULT_DELIMITER` setzen.
149 156
150Der Rest der Schleife bleibt gleich. 157Der Rest der Schleife bleibt gleich.
151 158
152**3\. Fehlerbehandlung hinzufügen** 159**3\. Fehlerbehandlung hinzufügen**
153 160
154Bei der Verarbeitung können verschiedene Fehler auftreten, die ein robustes Programm selbstverständlich behandelt. Leider stellt die Programmiersprache C keinen Exception-Mechanismus zur Verfügung, so dass sich der Programmierer an dieser Stelle anders behelfen muss. 161Bei der Verarbeitung können verschiedene Fehler auftreten, die ein robustes Programm selbstverständlich behandelt.
162Leider stellt die Programmiersprache C keinen Exception-Mechanismus zur Verfügung, so dass sich der Programmierer an dieser Stelle anders behelfen muss.
155 163
156Die beste Variante, die dem klassischen Exceptions am nächsten kommt, sind die beiden C-Funktionen `setjmp()` und `longjmp()`, mit deren Hilfe wir nun die Fehlerbehandlung umsetzen. Wir benötigen dafür die beiden Felder `cb_error` und `cb_error_arg` des `csv_options`-Objektes: 164Die beste Variante, die den klassischen Exceptions am nächsten kommt, sind die beiden C-Funktionen `setjmp()` und `longjmp()`, mit deren Hilfe wir nun die Fehlerbehandlung umsetzen.
165Wir benötigen dafür die beiden Felder `cb_error` und `cb_error_arg` des `csv_options`-Objektes:
157 166
158~~~c 167~~~c
159jmp_buf env; 168jmp_buf env;
@@ -161,18 +170,20 @@ csv_options.cb_error = csvErrorHandler;
161csv_options.cb_error_arg = &env; 170csv_options.cb_error_arg = &env;
162~~~ 171~~~
163 172
164`cb_error` ist ein Zeiger auf eine Callback-Funktion, die im Falle eines Fehlers aus der CSV-Bibliothek heraus aufgerufen wird. Ihr werden ein Fehlerwert vom Typ `csv_err_t` sowie ein Zeiger auf benutzerdefinierte Daten -- in unserem Fall ein Zeiger auf eine `jmp_buf`-Variable -- mit übergeben: 173`cb_error` ist ein Zeiger auf eine Callback-Funktion, die im Falle eines Fehlers aus der CSV-Bibliothek heraus aufgerufen wird.
174Ihr werden ein Fehlerwert vom Typ `csv_err_t` sowie ein Zeiger auf benutzerdefinierte Daten -- in unserem Fall ein Zeiger auf eine `jmp_buf`-Variable -- mit übergeben:
165 175
166~~~c 176~~~c
167static void 177static void
168csvErrorHandler(csv_err_t err, void *cb_arg) 178csvErrorHandler(csv_err_t err, void *cb_error_arg)
169{ 179{
170 fprintf(stderr, "error while csv-processing: %s\n", csv_err_str(err)); 180 fprintf(stderr, "error while csv-processing: %s\n", csv_err_str(err));
171 longjmp(*((jmp_buf *) cb_arg), err); 181 longjmp(*((jmp_buf *) cb_error_arg), err);
172} 182}
173~~~ 183~~~
174 184
175Mit Hilfe des Parameter `err` wird eine einfache Fehlermeldung protokolliert. Anschließend rufen wir `longjmp()` auf, damit die Fehlerbehandlung auf höherer Ebene fortgesetzt wird. 185Mit Hilfe des Parameter `err` wird eine einfache Fehlermeldung protokolliert.
186Anschließend rufen wir `longjmp()` auf, damit die Fehlerbehandlung auf höherer Ebene fortgesetzt wird.
176 187
177Die Hauptschleife in unserer Funktion sieht nun so aus: 188Die Hauptschleife in unserer Funktion sieht nun so aus:
178 189
@@ -216,7 +227,7 @@ case CSV_ERR_IO_READ:
216 227
217### Typen 228### Typen
218 229
219**Fehlercodes** für die Callbackhandler: 230**Fehlercodes** für die Callback-Handler:
220 231
221~~~c 232~~~c
222typedef enum { 233typedef enum {
@@ -247,7 +258,7 @@ typedef struct {
247extern const csv_options_t csv_default_options; 258extern const csv_options_t csv_default_options;
248~~~ 259~~~
249 260
250**Contextobjekt** zur Aufnahme der aktuell gelesenen Zeile: 261**Context Objekt** zur Aufnahme der aktuell gelesenen Zeile:
251 262
252~~~c 263~~~c
253typedef struct { 264typedef struct {
@@ -284,9 +295,10 @@ void csv_cleanup(csv_t *csv);
284Gibt den belegten Speicher eines `csv_t`-Objektes wieder frei. 295Gibt den belegten Speicher eines `csv_t`-Objektes wieder frei.
285 296
286Parameter 297Parameter
287: **`csv`** ein Zeiger initialisiertes `csv_t`-Objekt. 298: **`csv`** ein Zeiger auf ein initialisiertes `csv_t`-Objekt.
288 299
289`csv_cleanup()` gibt den verwendeten Speicher des csv-Objektes wieder frei. Ein direkter Aufruf dieser Funktion ist nur im Fehlerfall notwendig weil dann die automatische Speicherfreigabe der Bibliothek nicht greift. 300`csv_cleanup()` gibt den verwendeten Speicher des csv-Objektes wieder frei.
301Ein direkter Aufruf dieser Funktion ist nur im Fehlerfall notwendig weil dann die automatische Speicherfreigabe der Bibliothek nicht greift.
290 302
291**Zeilenweises Lesen** 303**Zeilenweises Lesen**
292 304
@@ -297,7 +309,7 @@ size_t csv_read(csv_t *csv, FILE *in);
297Liest einen Datensatz aus dem FILE-Stream, speichert das Ergebnis in `csv` und liefert die Anzahl der gefundenen Felder zurück. 309Liest einen Datensatz aus dem FILE-Stream, speichert das Ergebnis in `csv` und liefert die Anzahl der gefundenen Felder zurück.
298 310
299Parameter 311Parameter
300: **`csv`** ein Zeiger initialisiertes `csv_t`-Objekt. 312: **`csv`** ein Zeiger auf ein initialisiertes `csv_t`-Objekt.
301: **`in`** ein Zeiger auf einen geöffneten C-FILE-Stream. 313: **`in`** ein Zeiger auf einen geöffneten C-FILE-Stream.
302 314
303Rückgabe 315Rückgabe
@@ -313,7 +325,7 @@ const char *csv_field(const csv_t *const csv, size_t idx);
313Die beiden Funktionen ermöglichen einen Zugriff auf die einzelnen Felder des zuletzt gelesenen Datensatzes. 325Die beiden Funktionen ermöglichen einen Zugriff auf die einzelnen Felder des zuletzt gelesenen Datensatzes.
314 326
315Parameter 327Parameter
316: **`csv`** ein Zeiger initialisiertes `csv_t`-Objekt. 328: **`csv`** ein Zeiger auf ein initialisiertes `csv_t`-Objekt.
317: **`idx`** Index des zu lesenden Feldes. (0 <= idx < `csv_nfields(&csv)`) 329: **`idx`** Index des zu lesenden Feldes. (0 <= idx < `csv_nfields(&csv)`)
318 330
319Rückgabe 331Rückgabe
@@ -342,11 +354,10 @@ Die CSV-Bibliothek kann an verschiedene Bedürfnisse angepasst werden.
342 354
343Beim Compilieren der Datei `csv.c` können die Standardwerte für Feldbegrenzer und Feldtrenner als Makrodefinitionen `CSV_DEFAULT_DELIMITER` und `CSV_DEFAULT_SEPARATOR` voreingestellt werden. 355Beim Compilieren der Datei `csv.c` können die Standardwerte für Feldbegrenzer und Feldtrenner als Makrodefinitionen `CSV_DEFAULT_DELIMITER` und `CSV_DEFAULT_SEPARATOR` voreingestellt werden.
344 356
345Option Beschreibung Standardwert 357| Option | Beschreibung | Standardwert |
346-------------------------- -------------- --------------------------------- 358| -------------------------- | -------------- | -------------|
347`CSV_DEFAULT_DELIMITER` Feldbegrenzer `"` 359| `CSV_DEFAULT_DELIMITER` | Feldbegrenzer | `"` |
348`CSV_DEFAULT_SEPARATOR` Feldtrenner `,` 360| `CSV_DEFAULT_SEPARATOR` | Feldtrenner | `,` |
349-------------------------- -------------- ---------------------------------
350Table: Makros 361Table: Makros
351 362
352Soll beispielsweise das in Deutschland viel häufiger vorkommende Semikolon (`;`) statt einem Komma (`,`) standardmäßig als Trennsymbol zwischen den Feldern einer CSV-Datei benutzt werden sieht der Aufruf zum Compilieren so aus: 363Soll beispielsweise das in Deutschland viel häufiger vorkommende Semikolon (`;`) statt einem Komma (`,`) standardmäßig als Trennsymbol zwischen den Feldern einer CSV-Datei benutzt werden sieht der Aufruf zum Compilieren so aus:
@@ -365,9 +376,11 @@ TODO!
365 376
366## Lizenz 377## Lizenz
367 378
368In den letzten Jahrzehnten habe ich massiv vom Internet und seinen Inhalten profitiert. Ein gutes Stück meiner Kenntnisse und Fähigkeiten verdanke ich den vielen Foren, Webseiten, Newsgroups und Individuen die Informationen und Software kostenfrei und allgemein zugänglich bereitstellen. 379In den letzten Jahrzehnten habe ich massiv vom Internet und seinen Inhalten profitiert.
380Ein gutes Stück meiner Kenntnisse und Fähigkeiten verdanke ich den vielen Foren, Webseiten, Newsgroups und Individuen die Informationen und Software kostenfrei und allgemein zugänglich bereitstellen.
369 381
370Aus diesem Grund stelle ich meine Software (sofern nicht anders angegeben) unter der freizügigen [Do What the Fuck You Want to Public License](http://www.wtfpl.net) zur Verfügung. Vielleicht findet jemand den Code nützlich oder kann Erkenntnisse daraus gewinnen. 382Aus diesem Grund stelle ich meine Software (sofern nicht anders angegeben) unter der freizügigen [Do What the Fuck You Want to Public License](http://www.wtfpl.net) zur Verfügung.
383Vielleicht findet jemand den Code nützlich oder kann Erkenntnisse daraus gewinnen.
371 384
372~~~ 385~~~
373Copyright © 2020 Thomas Schmucker 386Copyright © 2020 Thomas Schmucker