aboutsummaryrefslogtreecommitdiff
path: root/csv-test.c
diff options
context:
space:
mode:
authorThomas Schmucker <ts@its1.de>2020-06-11 18:27:39 +0200
committerThomas Schmucker <ts@its1.de>2020-06-11 18:27:39 +0200
commit5b06a00890fe3e2c77c7c5835c975e87b8c7dfb0 (patch)
treecfe688d2c6bd3116ce81bc709f13a1fa1c68a718 /csv-test.c
downloadlibcsv-5b06a00890fe3e2c77c7c5835c975e87b8c7dfb0.tar.gz
libcsv-5b06a00890fe3e2c77c7c5835c975e87b8c7dfb0.tar.bz2
libcsv-5b06a00890fe3e2c77c7c5835c975e87b8c7dfb0.zip
erster Commit
Diffstat (limited to 'csv-test.c')
-rw-r--r--csv-test.c358
1 files changed, 358 insertions, 0 deletions
diff --git a/csv-test.c b/csv-test.c
new file mode 100644
index 0000000..97cc558
--- /dev/null
+++ b/csv-test.c
@@ -0,0 +1,358 @@
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <time.h>
5#include <stdbool.h>
6
7/* === CSV-STRING Interface === */
8
9typedef struct {
10 char *str;
11 int cap, pos;
12} csv_string_t;
13
14static void
15csv_string_init(csv_string_t *csv_string)
16{
17 static const int INITIAL_CAP = 16;
18
19 if ( (csv_string->str = malloc(INITIAL_CAP)) != NULL ) {
20 csv_string->cap = INITIAL_CAP;
21 csv_string->pos = 0;
22 }
23}
24
25static void
26csv_string_reset(csv_string_t *csv_string)
27{
28 csv_string->pos = 0;
29}
30
31static bool
32csv_string_empty(csv_string_t *csv_string)
33{
34 return ( csv_string->pos == 0 ) ? true : false;
35}
36
37inline static void
38csv_string_append(csv_string_t *csv_string, int ch)
39{
40 if ( csv_string->pos == csv_string->cap ) { /* grow if needed */
41 int cap = (csv_string->cap * 3) / 2; /* *= 1.5 */
42 char *str = realloc(csv_string->str, cap);
43 if ( str == NULL ) {
44 return;
45 }
46 csv_string->str = str;
47 csv_string->cap = cap;
48 }
49
50 csv_string->str[csv_string->pos++] = ch; /* append char */
51}
52
53static void
54csv_string_free(csv_string_t *csv_string)
55{
56 free(csv_string->str);
57}
58
59/* === CSV-FIELD Interface === */
60
61typedef struct {
62 int *fields;
63 int cap, pos;
64} csv_field_t;
65
66static void
67csv_field_init(csv_field_t *csv_field)
68{
69 static const size_t INITIAL_CAP = 16;
70
71 if ( (csv_field->fields = malloc(INITIAL_CAP * sizeof(csv_field->fields[0]))) != NULL ) {
72 csv_field->cap = INITIAL_CAP;
73 csv_field->pos = 0;
74 }
75}
76
77static void
78csv_field_reset(csv_field_t *csv_field)
79{
80 csv_field->pos = 0;
81}
82
83static void
84csv_field_append(csv_field_t *csv_field, int idx)
85{
86 if ( csv_field->pos == csv_field->cap ) { /* grow if needed */
87 int cap = (csv_field->cap * 3) / 2; /* *= 1.5 */
88 int *fields = realloc(csv_field->fields, cap * sizeof(csv_field->fields[0]));
89 if ( fields == NULL ) {
90 return;
91 }
92 csv_field->fields = fields;
93 csv_field->cap = cap;
94 }
95
96 csv_field->fields[csv_field->pos++] = idx; /* append idx */
97}
98
99static void
100csv_field_free(csv_field_t *csv_field)
101{
102 free(csv_field->fields);
103}
104
105/* === CSV Interface === */
106
107typedef struct {
108 char quote_symbol;
109 char sep_symbol;
110} csv_options_t;
111
112static csv_options_t default_csv_options = {
113 .quote_symbol = '"',
114 .sep_symbol = ','
115};
116
117typedef struct {
118 csv_options_t *csv_options;
119 csv_string_t csv_string;
120 csv_field_t csv_field;
121} csv_t;
122
123void
124csv_init_opt(csv_t *csv, csv_options_t *csv_options)
125{
126 csv->csv_options = ( csv_options != NULL ) ? csv_options : &default_csv_options;
127
128 csv_string_init(&csv->csv_string);
129 csv_field_init(&csv->csv_field);
130}
131
132void
133csv_init(csv_t *csv)
134{
135 csv_init_opt(csv, &default_csv_options);
136}
137
138void
139csv_free(csv_t *csv)
140{
141 csv_string_free(&csv->csv_string);
142 csv_field_free(&csv->csv_field);
143}
144
145int
146csv_nfields(csv_t *csv)
147{
148 if ( csv->csv_string.pos != 0 ) {
149 return csv->csv_field.pos;
150 }
151 return 0;
152}
153
154const char *
155csv_field(csv_t *csv, int idx)
156{
157 return &csv->csv_string.str[csv->csv_field.fields[idx]];
158}
159
160int
161csv_read(csv_t *csv, FILE *in)
162{
163 enum {
164 ZST_START_FIELD,
165 ZST_QUOTED_FIELD,
166 ZST_SIMPLE_FIELD,
167 ZST_END_FIELD,
168 ZST_END_LINE,
169 ZST_END_FILE,
170 };
171
172 register const int QUOTE = csv->csv_options->quote_symbol;
173 register const int SEP = csv->csv_options->sep_symbol;
174
175 csv_string_reset(&csv->csv_string);
176 csv_field_reset(&csv->csv_field);
177
178 for ( int zst = ZST_START_FIELD; ; ) {
179 int ch;
180
181 switch ( zst ) {
182 case ZST_START_FIELD:
183 csv_field_append(&csv->csv_field, csv->csv_string.pos);
184
185 ch = fgetc(in);
186 if ( ch == EOF ) {
187 zst = ZST_END_FILE;
188 }
189 else if ( ch == '\r' ) { /* Teste auf CR.. */
190 ch = fgetc(in);
191 if ( ch != '\n' ) { /* ... LF */
192 ungetc(ch, in);
193 }
194 zst = ZST_END_LINE;
195 }
196 else if ( ch == '\n' ) {
197 zst = ZST_END_LINE;
198 }
199 else if ( ch == SEP ) {
200 zst = ZST_END_FIELD;
201 }
202 else if ( ch == QUOTE ) {
203 zst = ZST_QUOTED_FIELD;
204 }
205 else {
206 csv_string_append(&csv->csv_string, ch);
207 zst = ZST_SIMPLE_FIELD;
208 }
209 break;
210
211 case ZST_QUOTED_FIELD:
212 do {
213 ch = fgetc(in);
214 if ( ch == EOF ) {
215 zst = ZST_END_FILE;
216 }
217 else if ( ch == QUOTE ) {
218 ch = fgetc(in);
219 if ( ch == EOF ) {
220 zst = ZST_END_FILE;
221 }
222 else if ( ch == QUOTE ) {
223 csv_string_append(&csv->csv_string, QUOTE);
224 }
225 else if ( ch == SEP ) {
226 zst = ZST_END_FIELD;
227 }
228 else if ( ch == '\r' ) {
229 ch = fgetc(in);
230 if ( ch != '\n' ) {
231 ungetc(ch, in);
232 }
233 zst = ZST_END_LINE;
234 }
235 else if ( ch == '\n' ) {
236 zst = ZST_END_LINE;
237 }
238 else {
239 csv_string_append(&csv->csv_string, QUOTE);
240 ungetc(ch, in); /* zuviel gelesenes Zeichen zurückstellen */
241 }
242 }
243 else {
244 csv_string_append(&csv->csv_string, ch);
245 }
246 } while ( zst == ZST_QUOTED_FIELD );
247 break;
248
249 case ZST_SIMPLE_FIELD:
250 do {
251 ch = fgetc(in);
252 if ( ch == EOF ) {
253 zst = ZST_END_FILE;
254 }
255 else if ( ch == SEP ) {
256 zst = ZST_END_FIELD;
257 }
258 else if ( ch == '\r' ) {
259 ch = fgetc(in);
260 if ( ch != '\n' ) {
261 ungetc(ch, in);
262 }
263 zst = ZST_END_LINE;
264 }
265 else if ( ch == '\n' ) {
266 zst = ZST_END_LINE;
267 }
268 else {
269 csv_string_append(&csv->csv_string, ch);
270 }
271 } while ( zst == ZST_SIMPLE_FIELD );
272 break;
273
274 case ZST_END_FIELD:
275 csv_string_append(&csv->csv_string, '\0');
276 zst = ZST_START_FIELD;
277 break;
278
279 case ZST_END_LINE:
280 csv_string_append(&csv->csv_string, '\0');
281 return csv->csv_field.pos;
282
283 case ZST_END_FILE:
284 if ( !csv_string_empty(&csv->csv_string) ) {
285 /* EOF wurde gelesen, allerdings sind Daten zur Vearbeitung vorhanden! */
286 csv_string_append(&csv->csv_string, '\0');
287 return csv->csv_field.pos;
288 }
289 return -1; /* EOF reached */
290 }
291 }
292}
293
294int
295main(void)
296{
297 csv_t csv[1];
298 int n, line = 0;
299
300#if 0
301 char data[] = "\"\"aaa\",\"b\"\"bb\",\"ccc\"\n"
302 "zzz,,yyy,xxx\n"
303 ",\n"
304 "";
305 FILE *in = fmemopen(data, strlen(data), "rb");
306
307 if ( in == NULL ) {
308 printf("no data...\n");
309 return 0;
310 }
311
312 csv_init(csv);
313 while ( (n = csv_read(csv, in)) != -1 ) {
314 printf("%4d: ", ++line);
315
316 for ( int i = 0; i < n; ++i ) {
317 printf("'%s' ", csv_field(csv, i));
318 }
319 putchar('\n');
320
321 if ( line == 100 )
322 break;
323 }
324 csv_free(csv);
325#endif
326
327#if 0
328 csv_init(csv);
329 while ( (n = csv_read(csv, stdin)) != -1 ) {
330 printf("%4d: ", ++line);
331
332 for ( int i = 0; i < n; ++i ) {
333 printf("'%s' ", csv_field(csv, i));
334 }
335 putchar('\n');
336
337 if ( line == 100 )
338 break;
339 }
340 csv_free(csv);
341#endif
342
343#if 1
344 clock_t start = clock();
345
346 csv_init(csv);
347 while ( (n = csv_read(csv, stdin)) != -1 ) {
348 ++line;
349 }
350 csv_free(csv);
351
352 clock_t end = clock();
353
354 printf("%d lines processed, duration: %.3lf sec\n", line, ((double)(end - start)) / CLOCKS_PER_SEC);
355
356 return EXIT_SUCCESS;
357#endif
358}