diff options
| author | Thomas Schmucker <ts@its1.de> | 2020-08-29 09:37:29 +0200 |
|---|---|---|
| committer | Thomas Schmucker <ts@its1.de> | 2020-08-29 09:37:29 +0200 |
| commit | 8b29decb942b2e598040d0fd8d01892796846833 (patch) | |
| tree | 6ebeced94605461894c2e534df1f6b30fdf873e4 | |
| parent | ff35dcfa63088beb23e140932bbc28d541b6874c (diff) | |
| download | libcsv-8b29decb942b2e598040d0fd8d01892796846833.tar.gz libcsv-8b29decb942b2e598040d0fd8d01892796846833.tar.bz2 libcsv-8b29decb942b2e598040d0fd8d01892796846833.zip | |
Aktualisiere Dokumentation
| -rw-r--r-- | doc/csv.md | 94 |
1 files changed, 76 insertions, 18 deletions
| @@ -13,27 +13,27 @@ Kurz: suckless! | |||
| 13 | 13 | ||
| 14 | ## Schnellstart für Ungeduldige | 14 | ## Schnellstart für Ungeduldige |
| 15 | 15 | ||
| 16 | 1\. Füge die beiden Dateien [csv.c](csv.c) und [csv.h](csv.h) Deinem C/C++ Projekt hinzu. | 16 | **1\. Füge die beiden Dateien [csv.c](csv.c) und [csv.h](csv.h) Deinem C/C++ Projekt hinzu.** |
| 17 | 17 | ||
| 18 | 2\. Inkludiere die Headerdatei `csv.h`. | 18 | **2\. Inkludiere die Headerdatei `csv.h`:** |
| 19 | 19 | ||
| 20 | ~~~ | 20 | ~~~ |
| 21 | #include "csv.h" | 21 | #include "csv.h" |
| 22 | ~~~ | 22 | ~~~ |
| 23 | 23 | ||
| 24 | 3\. Erstelle und initialisiere ein CSV-Objekt. | 24 | **3\. Erstelle und initialisiere ein CSV-Objekt:** |
| 25 | 25 | ||
| 26 | ~~~ | 26 | ~~~ |
| 27 | csv_t csv = { 0 }; oder csv_t csv; | 27 | csv_t csv = { 0 }; oder csv_t csv; |
| 28 | csv_init(&csv); | 28 | csv_init(&csv); |
| 29 | ~~~ | 29 | ~~~ |
| 30 | 30 | ||
| 31 | 4\. Verarbeite die CSV-Datei | 31 | **4\. Verarbeite die CSV-Datei:** |
| 32 | 32 | ||
| 33 | ~~~ | 33 | ~~~ |
| 34 | for ( size_t nf; (nf = csv_read(&csv, in)) != 0; ) { /* eine Zeile aus `in` lesen */ | 34 | for ( size_t nf; (nf = csv_read(&csv, in)) != 0; ) { /* eine Zeile aus `in` lesen */ |
| 35 | for ( size_t i = 0; i != nf; i++ ) { /* `nf` beinhaltet die Feldanzahl */ | 35 | for ( size_t i = 0; i != nf; i++ ) { /* `nf` beinhaltet die Feldanzahl */ |
| 36 | const char *s = csv_field(&csv, i); /* Feld `i` der gelesenen Zeile ansprechen */ | 36 | const char *s = csv_field(&csv, i); /* Feld `i` der gelesenen Zeile ansprechen */ |
| 37 | /* ... */ | 37 | /* ... */ |
| 38 | } | 38 | } |
| 39 | } | 39 | } |
| @@ -45,15 +45,16 @@ Es sind nur zwei Funktionen notwendig, um die Daten des FILE-Streams `in` vollst | |||
| 45 | 45 | ||
| 46 | Der 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. | 46 | Der 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. |
| 47 | 47 | ||
| 48 | Mehr 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. | ||
| 49 | |||
| 48 | ## Motivation | 50 | ## Motivation |
| 49 | 51 | ||
| 50 | Die meisten anderen CSV-Bibliotheken passen nicht: | 52 | Die meisten anderen CSV-Bibliotheken passen nicht: |
| 51 | 53 | ||
| 52 | - falsche Verarbeitung bei eingeklammerten Feldern: | 54 | - falsche Verarbeitung bei eingeklammerten Feldern: |
| 53 | C++: `getline()` -> `stringstream` -> `getline()`; | 55 | C++: `getline()` -> `stringstream` -> `getline()`; |
| 54 | C: `fgets()` -> `strtok`() | 56 | C: `fgets()` -> `strtok()` |
| 55 | Irgendwelche regulären Ausdrücke | 57 | - fehlerhafte Verarbeitung von "problematischen" CSV-Dateien (Zeilenumbrüche innerhalb einer Zelle) |
| 56 | - fehlerhafte Verarbeitung von "problematischen" CSV-Dateien (Linebreaks innerhalb einer Zelle) | ||
| 57 | - unvorteilhafte Lizenz (GPL) | 58 | - unvorteilhafte Lizenz (GPL) |
| 58 | - umständliche API (Callbacks zum Verarbeiten der Inhalte, seltsames Handling am Dateiende) | 59 | - umständliche API (Callbacks zum Verarbeiten der Inhalte, seltsames Handling am Dateiende) |
| 59 | - globale Variablen, die den Einsatz in Multi-Threadumgebungen unmöglich machen | 60 | - globale Variablen, die den Einsatz in Multi-Threadumgebungen unmöglich machen |
| @@ -62,7 +63,7 @@ Die meisten anderen CSV-Bibliotheken passen nicht: | |||
| 62 | - Langsam, Aufgebläht | 63 | - Langsam, Aufgebläht |
| 63 | - schlecht oder gar nicht anpassbar | 64 | - schlecht oder gar nicht anpassbar |
| 64 | 65 | ||
| 65 | => Selber schreiben! | 66 | Lösung: => Selber schreiben! |
| 66 | 67 | ||
| 67 | ## Tutorial | 68 | ## Tutorial |
| 68 | 69 | ||
| @@ -81,7 +82,7 @@ processFile(const char *filename) | |||
| 81 | return false; | 82 | return false; |
| 82 | } | 83 | } |
| 83 | 84 | ||
| 84 | // [ HIERHER KOMMT DER CSV-HANDLING CODE ] | 85 | // HIERHER KOMMT DER CSV-HANDLING CODE |
| 85 | 86 | ||
| 86 | fclose(in); | 87 | fclose(in); |
| 87 | return true; | 88 | return true; |
| @@ -103,13 +104,27 @@ csv_cleanup(&csv); | |||
| 103 | 104 | ||
| 104 | Dieser Code erzeugt ein neues `csv_t`-Objekt im Speicher welches anschließend mit `csv_init()` mit Standardwerten initialisiert wird. | 105 | Dieser Code erzeugt ein neues `csv_t`-Objekt im Speicher welches anschließend mit `csv_init()` mit Standardwerten initialisiert wird. |
| 105 | 106 | ||
| 106 | Dann wird der Datenstrom `in` zeilenweise in den Speicher eingelesen und innerhalb der Schleife verarbeitet. Die Anzahl der Felder pro Zeile wird in der Schleife mit Hilfe der Funktion `csv_nfields()` ermittelt. Alternativ liefert auch der Returnwert des vorherigen `csv_read()`-Aufrufs die Feldanzahl. | 107 | Dann 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: |
| 108 | |||
| 109 | ~~~ | ||
| 110 | // ... | ||
| 111 | size_t nf; | ||
| 112 | while ( (nf = csv_read(&csv, in)) != 0 ) { | ||
| 113 | // ... | ||
| 114 | } | ||
| 115 | // ... | ||
| 116 | ~~~ | ||
| 117 | |||
| 118 | Der Rückgabewert 0 bei einem Aufruf von `csv_read()` kennzeichnet das Ende des Datenstroms. | ||
| 119 | |||
| 120 | > **Hinweis:** | ||
| 121 | > 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. | ||
| 107 | 122 | ||
| 108 | Der 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. | 123 | Der 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. |
| 109 | 124 | ||
| 110 | **2\. Optionen einstellen** | 125 | **2\. Optionen einstellen** |
| 111 | 126 | ||
| 112 | Wenn 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 (`;`) als Feldtrenner. | 127 | Wenn 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. |
| 113 | 128 | ||
| 114 | Wir berücksichtigen dies bei der Initialisierung eines `csv_options_t`-Objektes. | 129 | Wir berücksichtigen dies bei der Initialisierung eines `csv_options_t`-Objektes. |
| 115 | 130 | ||
| @@ -122,7 +137,7 @@ csv_t csv; | |||
| 122 | csv_init_opt(&csv, &csv_options); | 137 | csv_init_opt(&csv, &csv_options); |
| 123 | ~~~ | 138 | ~~~ |
| 124 | 139 | ||
| 125 | Das `csv`-Objekt wird nun mit der Funktion `csv_init_opt()` initialisiert, die einen zusätzlichen Zeiger auf ein `csv_options_t`-Objekt erwartet. Damit wir nicht alle Felder von `csv_options` ändern müssen, initialisieren wir dieses zunächst mit `csv_default_options`. | 140 | Damit 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` 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. |
| 126 | 141 | ||
| 127 | > **Hinweis:** | 142 | > **Hinweis:** |
| 128 | > `csv_init(&csv);` ist letztlich nur eine kürzere Schreibweise für `csv_init_opt(&csv, &csv_default_options);`. | 143 | > `csv_init(&csv);` ist letztlich nur eine kürzere Schreibweise für `csv_init_opt(&csv, &csv_default_options);`. |
| @@ -131,8 +146,7 @@ Der Rest der Schleife bleibt gleich. | |||
| 131 | 146 | ||
| 132 | **3\. Fehlerbehandlung hinzufügen** | 147 | **3\. Fehlerbehandlung hinzufügen** |
| 133 | 148 | ||
| 134 | Bei der Verarbeitung können verschiedene Fehler auftreten, die ein robustes Programm selbstverständlich behandelt. | 149 | Bei 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. |
| 135 | Leider stellt die Programmiersprache C keinen Exception-Mechanismus zur Verfügung, so dass sich der Programmierer an dieser Stelle anders behelfen muss. | ||
| 136 | 150 | ||
| 137 | Die 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: | 151 | Die 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: |
| 138 | 152 | ||
| @@ -246,18 +260,44 @@ void csv_init(csv_t *csv); | |||
| 246 | void csv_init_opt(csv_t *csv, const csv_options_t *const csv_options); | 260 | void csv_init_opt(csv_t *csv, const csv_options_t *const csv_options); |
| 247 | ~~~ | 261 | ~~~ |
| 248 | 262 | ||
| 263 | Initialisiert ein `csv_t`-Objekt. | ||
| 264 | |||
| 265 | Parameter | ||
| 266 | : **`csv`** ein Zeiger vom Typ `csv_t`. | ||
| 267 | : **`csv_options`** ein Zeiger vom Typ `csv_options_t` | ||
| 268 | |||
| 269 | `csv_init(&c);` ist eine kürzere Schreibweise für `csv_init_opt(&c, &csv_csv_default_options);` | ||
| 270 | |||
| 271 | Mit der Funktion `csv_init_opt()` und einem angepassten `csv_options_t`-Objektes kann das Verhalten des CSV-Parsers zur Laufzeit parametrisiert werden. | ||
| 272 | |||
| 249 | **Manuelles Freigeben** | 273 | **Manuelles Freigeben** |
| 250 | 274 | ||
| 251 | ~~~ | 275 | ~~~ |
| 252 | void csv_cleanup(csv_t *csv); | 276 | void csv_cleanup(csv_t *csv); |
| 253 | ~~~ | 277 | ~~~ |
| 254 | 278 | ||
| 279 | Gibt den belegten Speicher eines `csv_t`-Objektes wieder frei. | ||
| 280 | |||
| 281 | Parameter | ||
| 282 | : **`csv`** ein Zeiger initialisiertes `csv_t`-Objekt. | ||
| 283 | |||
| 284 | `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. | ||
| 285 | |||
| 255 | **Zeilenweises Lesen** | 286 | **Zeilenweises Lesen** |
| 256 | 287 | ||
| 257 | ~~~ | 288 | ~~~ |
| 258 | size_t csv_read(csv_t *csv, FILE *in); | 289 | size_t csv_read(csv_t *csv, FILE *in); |
| 259 | ~~~ | 290 | ~~~ |
| 260 | 291 | ||
| 292 | Liest einen Datensatz aus dem FILE-Stream, speichert das Ergebnis in `csv` und liefert die Anzahl der gefundenen Felder zurück. | ||
| 293 | |||
| 294 | Parameter | ||
| 295 | : **`csv`** ein Zeiger initialisiertes `csv_t`-Objekt. | ||
| 296 | : **`in`** ein Zeiger auf einen geöffneten C-FILE-Stream. | ||
| 297 | |||
| 298 | Rückgabe | ||
| 299 | : Anzahl der gelesenen CSV-Felder einer Zeile oder 0 bei Dateiende. | ||
| 300 | |||
| 261 | **Feldzugriff** | 301 | **Feldzugriff** |
| 262 | 302 | ||
| 263 | ~~~ | 303 | ~~~ |
| @@ -265,19 +305,37 @@ size_t csv_nfields(const csv_t *const csv); | |||
| 265 | const char *csv_field(const csv_t *const csv, size_t idx); | 305 | const char *csv_field(const csv_t *const csv, size_t idx); |
| 266 | ~~~ | 306 | ~~~ |
| 267 | 307 | ||
| 308 | Die beiden Funktionen ermöglichen einen Zugriff auf die einzelnen Felder des zuletzt gelesenen Datensatzes. | ||
| 309 | |||
| 310 | Parameter | ||
| 311 | : **`csv`** ein Zeiger initialisiertes `csv_t`-Objekt. | ||
| 312 | : **`idx`** Index des zu lesenden Feldes. (0 <= idx < `csv_nfields(&csv)`) | ||
| 313 | |||
| 314 | Rückgabe | ||
| 315 | : `csv_nfields()` liefert die Anzahl der Felder des zuletzt gelesenen Datensatzes. | ||
| 316 | : `csv_field()` liefert einen Zeiger auf das Feld `idx` des zuletzt gelesenen Datensatzes. | ||
| 317 | |||
| 268 | **Fehlercodes als Textmeldung** | 318 | **Fehlercodes als Textmeldung** |
| 269 | 319 | ||
| 270 | ~~~ | 320 | ~~~ |
| 271 | const char *csv_err_str(csv_err_t csv_err); | 321 | const char *csv_err_str(csv_err_t csv_err); |
| 272 | ~~~ | 322 | ~~~ |
| 273 | 323 | ||
| 324 | Liefere eine textuelle Meldung zum Fehlercode `csv_err`. | ||
| 325 | |||
| 326 | Parameter | ||
| 327 | : **`csv_err`** ein Fehlercode vom Typ `csv_err_t`. | ||
| 328 | |||
| 329 | Rückgabe | ||
| 330 | : Zeiger auf die entspreche Fehlermeldung. | ||
| 331 | |||
| 274 | ## Anpassungen | 332 | ## Anpassungen |
| 275 | 333 | ||
| 276 | Die CSV-Bibliothek kann an verschiedene Bedürfnisse angepasst werden. | 334 | Die CSV-Bibliothek kann an verschiedene Bedürfnisse angepasst werden. |
| 277 | 335 | ||
| 278 | ### Übersetzungsoptionen | 336 | ### Übersetzungsoptionen |
| 279 | 337 | ||
| 280 | Beim 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. | 338 | Beim 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. |
| 281 | 339 | ||
| 282 | Option Beschreibung Standardwert | 340 | Option Beschreibung Standardwert |
| 283 | -------------------------- -------------- --------------------------------- | 341 | -------------------------- -------------- --------------------------------- |
| @@ -286,7 +344,7 @@ Option Beschreibung Standardwert | |||
| 286 | -------------------------- -------------- --------------------------------- | 344 | -------------------------- -------------- --------------------------------- |
| 287 | Table: Makros | 345 | Table: Makros |
| 288 | 346 | ||
| 289 | Soll beispielsweise das in Deutschland viel häufiger vorkommende Semikolon (`;`) statt einem Komma (`,`) als Trennsymbol zwischen den Feldern einer CSV-Datei benutzt werden sieht der Aufruf zum Compilieren so aus: | 347 | Soll 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: |
| 290 | 348 | ||
| 291 | ~~~ | 349 | ~~~ |
| 292 | cc -DCSV_DEFAULT_SEPARATOR="';'" -o csv-german.o -c csv.c | 350 | cc -DCSV_DEFAULT_SEPARATOR="';'" -o csv-german.o -c csv.c |
