-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathprint_my_column.c
143 lines (126 loc) · 4.13 KB
/
print_my_column.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/**
* Simple example using libzsv to call a row handler after each row is parsed
*
* In this example, we will use libzsv to parse a CSV file, look for a specified
* column of data, and for each row of data, output only that column
*
* Example:
* `echo "hi,there,you\na,b,c\nd,e,f" | ./print_my_column there
* Outputs:
* there
*
* In our implementation, we will define two row handlers. The first handler
* will be used to handle the first row, which we assume to be the header,
* and will search for the target column to identify what position it is in
* (or whether we can't find it). The second handler will be used to process
* each data row, and will output the target column
*/
static void print_my_column(void *ctx);
static void find_my_column(void *ctx);
#include <stdio.h>
#include <zsv.h>
#include <string.h>
/**
* First, we define a structure that will contain all the information that
* our row handler will need
*/
struct my_data {
zsv_parser parser;
const char *target_column_name; /* name of column to find and output */
size_t target_column_position; /* will be set to the position of the column to output */
char not_found; /* if we can't find the column, we'll set this flag */
};
/**
* Our first callback will search each cell in the (header) row. If it finds a match
* it sets the `target_column_position`, otherwise it halts further processing and
* sets the `not_found` flag
*/
static void find_my_column(void *ctx) {
struct my_data *data = ctx;
size_t target_column_name_len = strlen(data->target_column_name);
/* iterate through each cell */
size_t cell_count = zsv_cell_count(data->parser);
char found = 0;
for (size_t i = 0; i < cell_count; i++) {
struct zsv_cell c = zsv_get_cell(data->parser, i);
if (c.len == target_column_name_len && !memcmp(data->target_column_name, c.str, c.len)) {
data->target_column_position = i;
found = 1;
break;
}
}
if (!found) {
/**
* We couldn't find the target column name in our header row. Output a message and abort
*/
fprintf(stderr, "Could not find column %.*s\n", (int)target_column_name_len, data->target_column_name);
/**
* Once `zsv_abort()` is called, the parser will no longer invoke any callbacks
* and `zsv_parse_more()` will return `zsv_status_cancelled`
*/
zsv_abort(data->parser);
/**
* set a flag that we will later use to determine our program's exit code
*/
data->not_found = 1;
} else {
/**
* we found the column we are looking for. print its name, and change our row handler
* by calling `zsv_set_row_handler()`
*/
printf("%s\n", data->target_column_name);
zsv_set_row_handler(data->parser, print_my_column);
}
}
/**
* Output data from the selected column
*/
static void print_my_column(void *ctx) {
struct my_data *data = ctx;
/* use zsv_get_cell() to get our cell data */
struct zsv_cell c = zsv_get_cell(data->parser, data->target_column_position);
/* print the data, followed by a newline */
printf("%.*s\n", (int)c.len, c.str);
}
/**
* Main routine. Our example will take a single argument specifying a column
* name, read from stdin, and output, for each row, the specified column
*/
int main(int argc, const char *argv[]) {
if (argc < 2) {
fprintf(stderr, "Usage: print_my_column column_name < input.csv\n");
fprintf(stderr,
"Example:\n"
" echo \"A,B,C\\nA1,B1,C1\\nA2,B2,\\nA3,,C3\\n,,C3\" | %s B\n\n",
argv[0]);
return 0;
}
/**
* Initialize context data
*/
struct my_data data = {0};
data.target_column_name = argv[1];
/**
* Initialize parser options
*/
struct zsv_opts opts = {0};
opts.row_handler = find_my_column;
opts.ctx = &data;
/**
* Create a parser
*/
data.parser = zsv_new(&opts);
/**
* Continuously parse our input until we have no more input
* or an error has occurred (such as not finding the specified
* column name in the first row)
*/
while (zsv_parse_more(data.parser) == zsv_status_ok)
;
/**
* Clean up
*/
zsv_finish(data.parser);
zsv_delete(data.parser);
return data.not_found;
}