Line data Source code
1 : /*************************************************************************
2 : *
3 : * Copyright (c) 2011-2012 Kohei Yoshida
4 : *
5 : * Permission is hereby granted, free of charge, to any person
6 : * obtaining a copy of this software and associated documentation
7 : * files (the "Software"), to deal in the Software without
8 : * restriction, including without limitation the rights to use,
9 : * copy, modify, merge, publish, distribute, sublicense, and/or sell
10 : * copies of the Software, and to permit persons to whom the
11 : * Software is furnished to do so, subject to the following
12 : * conditions:
13 : *
14 : * The above copyright notice and this permission notice shall be
15 : * included in all copies or substantial portions of the Software.
16 : *
17 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 : * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19 : * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 : * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21 : * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 : * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 : * OTHER DEALINGS IN THE SOFTWARE.
25 : *
26 : ************************************************************************/
27 :
28 : #ifndef __ORCUS_CSV_PARSER_HPP__
29 : #define __ORCUS_CSV_PARSER_HPP__
30 :
31 : #define ORCUS_DEBUG_CSV 0
32 :
33 : #include <cstdlib>
34 : #include <cstring>
35 : #include <exception>
36 : #include <string>
37 : #include <cassert>
38 : #include <sstream>
39 :
40 : #include "cell_buffer.hpp"
41 :
42 : #if ORCUS_DEBUG_CSV
43 : #include <iostream>
44 : using std::cout;
45 : using std::endl;
46 : #endif
47 :
48 : namespace orcus {
49 :
50 64 : struct csv_parser_config
51 : {
52 : std::string delimiters;
53 : char text_qualifier;
54 : bool trim_cell_value:1;
55 :
56 64 : csv_parser_config() :
57 64 : trim_cell_value(false) {}
58 : };
59 :
60 : class csv_parse_error : public std::exception
61 : {
62 : std::string m_msg;
63 : public:
64 0 : csv_parse_error(const std::string& msg) : m_msg(msg) {}
65 0 : virtual ~csv_parse_error() throw() {}
66 0 : virtual const char* what() const throw() { return m_msg.c_str(); }
67 : };
68 :
69 : template<typename _Handler>
70 64 : class csv_parser
71 : {
72 : public:
73 : typedef _Handler handler_type;
74 :
75 : csv_parser(const char* p, size_t n, handler_type& hdl, const csv_parser_config& config);
76 : void parse();
77 :
78 : private:
79 26662 : bool has_char() const { return m_pos < m_length; }
80 406 : bool has_next() const { return m_pos + 1 < m_length; }
81 : void next();
82 : char cur_char() const;
83 : char next_char() const;
84 :
85 : bool is_delim(char c) const;
86 : bool is_text_qualifier(char c) const;
87 :
88 : // handlers
89 : void row();
90 : void cell();
91 : void quoted_cell();
92 :
93 : void parse_cell_with_quote(const char* p0, size_t len0);
94 : void skip_blanks();
95 :
96 : /**
97 : * Push cell value to the handler.
98 : */
99 : void push_cell_value(const char* p, size_t n);
100 :
101 178 : static bool is_blank(char c)
102 : {
103 178 : return c == ' ' || c == '\t';
104 : }
105 :
106 : private:
107 : handler_type& m_handler;
108 : const csv_parser_config& m_config;
109 : cell_buffer m_cell_buf;
110 : const char* mp_char;
111 : size_t m_pos;
112 : size_t m_length;
113 : };
114 :
115 : template<typename _Handler>
116 64 : csv_parser<_Handler>::csv_parser(const char* p, size_t n, handler_type& hdl, const csv_parser_config& config) :
117 64 : m_handler(hdl), m_config(config), mp_char(p), m_pos(0), m_length(n) {}
118 :
119 : template<typename _Handler>
120 64 : void csv_parser<_Handler>::parse()
121 : {
122 : #if ORCUS_DEBUG_CSV
123 : const char* p = mp_char;
124 : for (size_t i = m_pos; i < m_length; ++i, ++p)
125 : std::cout << *p;
126 : std::cout << std::endl;
127 : #endif
128 :
129 64 : m_handler.begin_parse();
130 794 : while (has_char())
131 666 : row();
132 64 : m_handler.end_parse();
133 64 : }
134 :
135 : template<typename _Handler>
136 25982 : void csv_parser<_Handler>::next()
137 : {
138 25982 : ++m_pos;
139 25982 : ++mp_char;
140 25982 : }
141 :
142 : template<typename _Handler>
143 41176 : char csv_parser<_Handler>::cur_char() const
144 : {
145 41176 : return *mp_char;
146 : }
147 :
148 : template<typename _Handler>
149 406 : char csv_parser<_Handler>::next_char() const
150 : {
151 406 : return *(mp_char+1);
152 : }
153 :
154 : template<typename _Handler>
155 17802 : bool csv_parser<_Handler>::is_delim(char c) const
156 : {
157 17802 : return m_config.delimiters.find(c) != std::string::npos;
158 : }
159 :
160 : template<typename _Handler>
161 15158 : bool csv_parser<_Handler>::is_text_qualifier(char c) const
162 : {
163 15158 : return m_config.text_qualifier == c;
164 : }
165 :
166 : template<typename _Handler>
167 666 : void csv_parser<_Handler>::row()
168 : {
169 666 : m_handler.begin_row();
170 7134 : while (true)
171 : {
172 7800 : if (is_text_qualifier(cur_char()))
173 178 : quoted_cell();
174 : else
175 7622 : cell();
176 :
177 7800 : if (!has_char())
178 : {
179 0 : m_handler.end_row();
180 0 : return;
181 : }
182 :
183 7800 : char c = cur_char();
184 7800 : if (c == '\n')
185 : {
186 666 : next();
187 : #if ORCUS_DEBUG_CSV
188 : cout << "(LF)" << endl;
189 : #endif
190 666 : m_handler.end_row();
191 666 : return;
192 : }
193 :
194 0 : assert(is_delim(c));
195 7134 : next();
196 :
197 7134 : if (m_config.trim_cell_value)
198 0 : skip_blanks();
199 : }
200 : }
201 :
202 : template<typename _Handler>
203 7622 : void csv_parser<_Handler>::cell()
204 : {
205 7622 : const char* p = mp_char;
206 7622 : size_t len = 0;
207 7622 : char c = cur_char();
208 26068 : while (c != '\n' && !is_delim(c))
209 : {
210 10824 : ++len;
211 10824 : next();
212 10824 : if (!has_char())
213 0 : break;
214 10824 : c = cur_char();
215 : }
216 :
217 7622 : if (!len)
218 5468 : p = NULL;
219 :
220 7622 : push_cell_value(p, len);
221 7622 : }
222 :
223 : template<typename _Handler>
224 178 : void csv_parser<_Handler>::quoted_cell()
225 : {
226 : #if ORCUS_DEBUG_CSV
227 : using namespace std;
228 : cout << "--- quoted cell" << endl;
229 : #endif
230 178 : char c = cur_char();
231 0 : assert(is_text_qualifier(c));
232 178 : next(); // Skip the opening quote.
233 178 : if (!has_char())
234 0 : return;
235 :
236 178 : const char* p0 = mp_char;
237 178 : size_t len = 1;
238 4124 : for (; has_char(); next(), ++len)
239 : {
240 2062 : c = cur_char();
241 : #if ORCUS_DEBUG_CSV
242 : cout << "'" << c << "'" << endl;
243 : #endif
244 2062 : if (!is_text_qualifier(c))
245 1884 : continue;
246 :
247 : // current char is a quote. Check if the next char is also a text
248 : // qualifier.
249 :
250 178 : if (has_next() && is_text_qualifier(next_char()))
251 : {
252 104 : next();
253 104 : parse_cell_with_quote(p0, len);
254 104 : return;
255 : }
256 :
257 : // Closing quote.
258 74 : m_handler.cell(p0, len-1);
259 74 : next();
260 74 : skip_blanks();
261 74 : return;
262 : }
263 :
264 : // Stream ended prematurely. Handle it gracefully.
265 0 : m_handler.cell(p0, len);
266 0 : next();
267 0 : skip_blanks();
268 : }
269 :
270 : template<typename _Handler>
271 104 : void csv_parser<_Handler>::parse_cell_with_quote(const char* p0, size_t len0)
272 : {
273 : #if ORCUS_DEBUG_CSV
274 : using namespace std;
275 : cout << "--- parse cell with quote" << endl;
276 : #endif
277 0 : assert(is_text_qualifier(cur_char()));
278 :
279 : // Push the preceding chars to the temp buffer.
280 104 : m_cell_buf.reset();
281 104 : m_cell_buf.append(p0, len0);
282 :
283 : // Parse the rest, until the closing quote.
284 104 : next();
285 104 : const char* p_cur = mp_char;
286 104 : size_t cur_len = 0;
287 4890 : for (; has_char(); next(), ++cur_len)
288 : {
289 4890 : char c = cur_char();
290 : #if ORCUS_DEBUG_CSV
291 : cout << "'" << c << "'" << endl;
292 : #endif
293 4890 : if (!is_text_qualifier(c))
294 4662 : continue;
295 :
296 228 : if (has_next() && is_text_qualifier(next_char()))
297 : {
298 : // double quotation. Copy the current segment to the cell buffer.
299 124 : m_cell_buf.append(p_cur, cur_len);
300 :
301 124 : next(); // to the 2nd quote.
302 124 : p_cur = mp_char;
303 124 : cur_len = 0;
304 124 : continue;
305 : }
306 :
307 : // closing quote. Flush the current segment to the cell
308 : // buffer, push the value to the handler, and exit normally.
309 104 : m_cell_buf.append(p_cur, cur_len);
310 :
311 104 : m_handler.cell(m_cell_buf.get(), m_cell_buf.size());
312 104 : next();
313 104 : skip_blanks();
314 104 : return;
315 : }
316 :
317 : // Stream ended prematurely.
318 0 : throw csv_parse_error("stream ended prematurely while parsing quoted cell.");
319 : }
320 :
321 : template<typename _Handler>
322 178 : void csv_parser<_Handler>::skip_blanks()
323 : {
324 178 : for (; has_char(); next())
325 : {
326 178 : if (!is_blank(*mp_char))
327 178 : break;
328 : }
329 178 : }
330 :
331 : template<typename _Handler>
332 7622 : void csv_parser<_Handler>::push_cell_value(const char* p, size_t n)
333 : {
334 7622 : size_t len = n;
335 :
336 7622 : if (m_config.trim_cell_value)
337 : {
338 : // Trim any leading blanks.
339 0 : for (size_t i = 0; i < n; ++i, --len, ++p)
340 : {
341 0 : if (!is_blank(*p))
342 0 : break;
343 : }
344 :
345 : // Trim any trailing blanks.
346 0 : if (len)
347 : {
348 0 : const char* p_end = p + (len-1);
349 0 : for (; p != p_end; --p_end, --len)
350 : {
351 0 : if (!is_blank(*p_end))
352 0 : break;
353 : }
354 : }
355 : }
356 :
357 7622 : m_handler.cell(p, len);
358 : #if ORCUS_DEBUG_CSV
359 : if (len)
360 : cout << "(cell:'" << std::string(p, len) << "')" << endl;
361 : else
362 : cout << "(cell:'')" << endl;
363 : #endif
364 7622 : }
365 :
366 : }
367 :
368 : #endif
|