* This tokenizer always copies the full "row" (all tokens). This makes * two things easier: * 1. It means that every word is guaranteed to be followed by a NUL character * (although it can include one as well). * 2. If usecols are used we can sniff the first row easier by parsing it * fully. Further, usecols can be negative so we may not know which row we * need up-front. * * Th
| 287 | * unicode flavors UCS1, UCS2, and UCS4 as a worthwhile optimization. |
| 288 | */ |
| 289 | NPY_NO_EXPORT int |
| 290 | npy_tokenize(stream *s, tokenizer_state *ts, parser_config *const config) |
| 291 | { |
| 292 | assert(ts->fields_size >= 2); |
| 293 | assert(ts->field_buffer_length >= 2*(Py_ssize_t)sizeof(Py_UCS4)); |
| 294 | |
| 295 | int finished_reading_file = 0; |
| 296 | |
| 297 | /* Reset to start of buffer */ |
| 298 | ts->field_buffer_pos = 0; |
| 299 | ts->num_fields = 0; |
| 300 | |
| 301 | while (true) { |
| 302 | /* |
| 303 | * This loop adds new fields to the result (to make up a full row) |
| 304 | * until the row ends (typically a line end or the file end) |
| 305 | */ |
| 306 | if (ts->state == TOKENIZE_INIT) { |
| 307 | /* Start a new field */ |
| 308 | if (add_field(ts) < 0) { |
| 309 | return -1; |
| 310 | } |
| 311 | ts->state = TOKENIZE_CHECK_QUOTED; |
| 312 | } |
| 313 | |
| 314 | if (NPY_UNLIKELY(ts->pos >= ts->end)) { |
| 315 | if (ts->buf_state == BUFFER_IS_LINEND && |
| 316 | ts->state != TOKENIZE_QUOTED) { |
| 317 | /* |
| 318 | * Finished line, do not read anymore (also do not eat \n). |
| 319 | * If we are in a quoted field and the "line" does not end with |
| 320 | * a newline, the quoted field will not have it either. |
| 321 | * I.e. `np.loadtxt(['"a', 'b"'], dtype="S2", quotechar='"')` |
| 322 | * reads "ab". This matches `next(csv.reader(['"a', 'b"']))`. |
| 323 | */ |
| 324 | break; |
| 325 | } |
| 326 | /* fetch new data */ |
| 327 | ts->buf_state = stream_nextbuf(s, |
| 328 | &ts->pos, &ts->end, &ts->unicode_kind); |
| 329 | if (ts->buf_state < 0) { |
| 330 | return -1; |
| 331 | } |
| 332 | if (ts->buf_state == BUFFER_IS_FILEEND) { |
| 333 | finished_reading_file = 1; |
| 334 | ts->pos = ts->end; /* stream should ensure this. */ |
| 335 | break; |
| 336 | } |
| 337 | else if (ts->pos == ts->end) { |
| 338 | /* This must be an empty line (and it must be indicated!). */ |
| 339 | assert(ts->buf_state == BUFFER_IS_LINEND); |
| 340 | break; |
| 341 | } |
| 342 | } |
| 343 | int status; |
| 344 | if (ts->unicode_kind == PyUnicode_1BYTE_KIND) { |
| 345 | status = tokenizer_core<Py_UCS1>(ts, config); |
| 346 | } |