File-Raw-Separated
view release on metacpan or search on metacpan
include/separated_parser.h view on Meta::CPAN
* Lenient mode (default): best-effort recovery. */
int strict;
/* Line ending mode. AUTO locks to first detected LF/CRLF/CR. */
separated_eol_t eol_mode;
/* Strip leading/trailing ASCII whitespace from unquoted fields only. */
int trim;
/* Empty unquoted field becomes the sentinel SEPARATED_FIELD_NULL
* (length == SEPARATED_FIELD_NULL_LEN, ptr == NULL).
* Quoted empty ("") stays a real zero-length field. */
int empty_is_undef;
/* Skip UTF-8 BOM detection / stripping. */
int binary;
/* Header mode: when set, the dispatcher (XS layer) consumes the
* first emitted row as field names and emits subsequent rows as
* hashrefs keyed by those names. The C parser itself is not
* affected â it always emits fields the same way. */
int header;
/* Maximum field length. 0 = use SEPARATED_FIELD_DEFAULT_CAP. */
size_t max_field_len;
} separated_options_t;
lib/File/Raw/Separated.pm view on Meta::CPAN
Strip leading/trailing ASCII space and tab from B<unquoted> fields
only. Quoted fields preserve all bytes. Default false.
=item C<empty_is_undef>
Empty unquoted field becomes C<undef>. Quoted empty (C<"">) stays the
empty string. Default false (always returns C<"">).
=item C<binary>
Skip UTF-8 BOM stripping and skip C<sv_utf8_decode> on each field.
Default false.
=item C<header>
Controls whether rows are emitted as arrayrefs (default) or hashrefs.
Two forms:
=over 4
=item C<< header => 1 >>
separated_parser.c view on Meta::CPAN
void *ud;
/* Field buffer (geometric growth). */
char *buf;
size_t buf_len;
size_t buf_cap;
/* State. */
parse_state_t state;
int field_was_quoted; /* 1 if current field began with a quote */
int bom_checked; /* 1 once we've decided about the BOM */
int any_field_in_row; /* 1 if at least one field started in this row */
/* Auto-detected / pinned EOL. */
separated_eol_t detected_eol;
int pending_cr; /* 1 if last byte was CR awaiting LF/data (CRLF detect) */
/* Diagnostics. */
size_t bytes_consumed;
size_t rows_emitted;
size_t err_offset;
separated_parser.c view on Meta::CPAN
ctx->field_was_quoted = 0;
ctx->any_field_in_row = 1;
if (end_of_row) {
ctx->rows_emitted++;
ctx->any_field_in_row = 0;
}
return SEPARATED_OK;
}
/* ------------------------------------------------------------
* BOM stripping (UTF-8 only, when binary=0).
* Called once before any byte is interpreted. Caller passes the
* incoming buffer pointer + length pair through bom_skip; on return
* any leading 3-byte BOM has been advanced past.
* ------------------------------------------------------------ */
static void
bom_check(separated_ctx_t *ctx, const char **pp, size_t *plen)
{
if (ctx->bom_checked) return;
ctx->bom_checked = 1;
if (ctx->opts.binary) return;
use strict;
use warnings;
use Test::More;
use File::Raw::Separated qw(import);
my $bom = "\xEF\xBB\xBF";
is_deeply(
file_csv_parse_buf("${bom}a,b\n"),
[['a', 'b']],
'UTF-8 BOM stripped from first field by default',
);
# binary => 1 keeps BOM bytes literal
my $rows = file_csv_parse_buf("${bom}a,b\n", { binary => 1 });
is($rows->[0][0], "${bom}a", 'binary mode preserves BOM bytes literally')
or diag(explain($rows));
is($rows->[0][1], 'b', 'second field unaffected');
# BOM in the middle of input is data, not stripped. In default
# (non-binary) mode the field gets sv_utf8_decode'd, so the 3-byte BOM
# decodes to the single Unicode codepoint U+FEFF.
is_deeply(
file_csv_parse_buf("a,${bom}b\n"),
[['a', "\x{feff}b"]],
'BOM in middle of input is data (decoded to U+FEFF)',
);
# In binary mode it stays as raw bytes.
is_deeply(
file_csv_parse_buf("a,${bom}b\n", { binary => 1 }),
[['a', "${bom}b"]],
'BOM in middle, binary mode keeps bytes',
);
done_testing;
( run in 0.693 second using v1.01-cache-2.11-cpan-9581c071862 )