### ----------------------------------------------------
### -- Text::CSV
### -- Licenses: Artistic-2.0
### -- Authors: H.Merijn Brand (Tux)
### -- File: doc/Text-CSV.md
### ----------------------------------------------------
### -- Chunk 1 of 3
# DISCLAIMER

Note that updating these docs is an ongoing process and some perl5 idioms
might not have been translated yet into correct raku idiom. My bad. Sorry.
(Feedback welcome)

# NAME

Text::CSV - comma-separated values manipulation routines

# SYNOPSIS

    use Text::CSV;

    # Read whole file in memory
    my @aoa = csv(in => "data.csv");  # as array of arrays
    my @aoh = csv(in => "data.csv",
                  headers => "auto"); # as array of hashes

    # Write array of arrays as csv file
    csv(in => @aoa, out => "file.csv", sep => ";");

    my @rows;
    # Read/parse CSV
    my $csv = Text::CSV.new;
    my $fh  = open "test.csv", :r, :!chomp;
    while (my @row = $csv.getline($fh)) {
        @row[2] ~~ m/pattern/ or next; # 3rd field should match
        @rows.push: @row;
        }
    $fh.close;

    # and write as CSV
    $fh = open "new.csv", :w;
    $csv.say($fh, $_) for @rows;
    $fh.close;

# DESCRIPTION

Text::CSV provides facilities for the composition and decomposition of
comma-separated values. An instance of the Text::CSV class will combine
fields into a `CSV` string and parse a `CSV` string into fields.

The module accepts either strings or files as input and support the use of
user-specified characters (or sequences thereof) for delimiters, separators,
and escapes.

In all following documentation, `WIP` stands for "Work In Progress" and
`NYI` for "Not Yet Implemented". The goal is to get rid of all of those.

## Embedded newlines

**Important Note**: The default behavior is to accept only UTF-8 characters.

But you still have the problem that you have to pass a correct line to the
["parse"](#parse) method, which is more complicated from the usual point of usage:

    my $csv = Text::CSV.new;
    for lines() : eager {          #  WRONG!
        $csv.parse($_);
        my @fields = $csv.fields;
        }

this will break for several reasons. The default open mode is to `chomp`
lines, which will also remove the newline sequence if that sequence is not
(part of) the newline at all. As the `for` might read broken lines: it
does not care about the quoting. If you need to support embedded newlines,
the way to go is to **not** pass [`eol`](#eol) in the parser (it accepts
`\n`, `\r`, **and** `\r\n` by default) and then

    my $csv = Text::CSV.new;
    my $io = open $file, :r, :!chomp;
    while (my $row = $csv.getline($io)) {
        my @fields = @$row;
        }

## Binary data

For now, Text::CSV only accepts Unicode. Binary data is planned.

# SPECIFICATION

While no formal specification for CSV exists, [RFC 4180](https://datatracker.ietf.org/doc/html/rfc4180)
(_1_) describes the common format and establishes  `text/csv` as the MIME
type registered with the IANA. [RFC 7111](https://datatracker.ietf.org/doc/html/rfc7111)
(_2_) adds fragments to CSV.

Many informal documents exist that describe the `CSV` format.   ["How To:
The Comma Separated Value (CSV) File Format"](https://metacpan.org/pod/creativyst.com#Doc-Articles-CSV-CSV01.shtml)
(_3_)  provides an overview of the  `CSV`  format in the most widely used
applications and explains how it can best be used and supported.

    1) https://datatracker.ietf.org/doc/html/rfc4180
    2) https://datatracker.ietf.org/doc/html/rfc7111
    3) http://creativyst.com/Doc/Articles/CSV/CSV01.shtml

The basic rules are as follows:

**CSV** is a delimited data format that has fields/columns separated by the
comma character and records/rows separated by newlines. Fields that contain
a special character (comma, newline, or double quote), must be enclosed in
double quotes. However, if a line contains a single entry that is the empty
string, it may be enclosed in double quotes. If a field's value contains a
double quote character it is escaped by placing another double quote
character next to it. The `CSV` file format does not require a specific
character encoding, byte order, or line terminator format.

- Each record is a single line ended by a line feed (ASCII/`LF`=`0x0A`) or
a carriage return and line feed pair (ASCII/`CRLF`=`0x0D 0x0A`), however,
line-breaks may be embedded.
- Fields are separated by commas.
- Allowable characters within a `CSV` field include `0x09` (`TAB`) and the
inclusive range of `0x20` (space) through `0x7E` (tilde). In binary mode
all characters are accepted, at least in quoted fields.
- A field within `CSV` must be surrounded by double-quotes to contain a
separator character (comma).

Though this is the most clear and restrictive definition, Text::CSV is way
more liberal than this, and allows extension:

- Line termination by a single carriage return is accepted by default
- The separation, escape, and escape can be any valid Unicode sequence.
- A field in `CSV` must be surrounded by double-quotes to make an embedded
double-quote, represented by a pair of consecutive double-quotes, valid.
You may additionally use the sequence `"0` for representation of a NULL
byte. Using `0x00` is just as valid.
- Several violations of the above specification may be lifted by passing some
options as attributes to the object constructor.

# METHODS

## version


Returns the current module version.

## new


Returns a new instance of class Text::CSV. The attributes are described by
the optional named parameters

    my $csv = Text::CSV.new(attributes ...);

The following attributes are available:

- eol


        my $csv = Text::CSV.new(eol => "\r\n");
                  $csv.eol(Str);
        my $eol = $csv.eol;

    The end-of-line string to add to rows for ["print"](#print) or the record separator
    for ["getline"](#getline).

    When not set in a **parser** instance, the default behavior is to accept
    `\n`, `\r`, and `\r\n`, so it is probably safer to not specify `eol` at
    all. Passing `Str` or the empty string behave the same.

    As raku interprets `\r\n` as a single grapheme in input, it is dissuaded
    to use `\r\n` as `eol` when parsing. Please choose `Str` instead.

    When not passed in a **generating** instance, records are not terminated at
    all, so it is probably wise to pass something you expect. A safe choice for
    `eol` on output is either `\n` or `\r\n`.

    Common values for `eol` are `"\012"` (`\n` or Line Feed), `"\015\012"`
    (`\r\n` or Carriage Return, Line Feed), and `"\015"` (`\r` or Carriage
    Return).

- sep

- sep\_char

- sep-char

- separator


        my $csv = Text::CSV.new(sep => ";");
                  $csv.sep("\x[ff0c]"); # FULLWIDTH COMMA
        my $sep = $csv.sep;

    The sequence used to separate fields, by default a comma: (`,`). This
    sequence is required and cannot be disabled.

    The separation sequence can not be equal to the quote sequence, the
    escape sequence or the newline sequence.

    See also ["CAVEATS"](#caveats)

- quote

- quote\_char

- quote-char


        my $csv = Text::CSV.new(quote => "'");
                  $csv.quote("\x[ff02]); # FULLWIDTH QUOTATION MARK
                  $csv.quote(Str);
        my $quo = $csv.quote;

    The sequence to quote fields containing blanks or binary data, by default
    the double quote character (`"`). A value of `Str` disables quotation
    (for simple cases only).

    The quotation sequence can not be equal to the separation sequence or the
    newline sequence.

    See also ["CAVEATS"](#caveats)

- escape

- escape\_char

- escape-char


        my $csv = Text::CSV.new(escape => "\\");
                  $csv.escape("\x[241b]");  # SYMBOL FOR ESCAPE
                  $csv.escape(Str);
        my $esc = $csv.escape;

    The sequence to escape certain characters inside quoted fields.

    The `escape` defaults to being the double-quote mark (`"`). In other
    words the same as the default sequence for [`quote`](#quote). This means
    that doubling the quote mark in a field escapes it:

        "foo","bar","Escape ""quote mark"" with two ""quote marks""","baz"

    If you change [`quote`](#quote) without changing `escape`, `escape` will
    still be the double-quote (`"`). If instead you want to escape
    [`quote`](#quote) by doubling it you will need to also change `escape` to
    be the same as what you have changed [`quote`](#quote) to.

    The escape sequence can not be equal to the separation sequence.

- binary


    WIP: Default is True. Non-UTF-8 real binary (Blob) does not yet parse.
    Opening the resource with encoding utf8-c8 is most likely the way to go.

        my $csv = Text::CSV.new(:binary);
                  $csv.binary(False);
        my $bin = $csv.binary;

    If this attribute is `True`, you may use binary data in quoted fields,
    including line feeds, carriage returns and `NULL` bytes. (The latter could
    be escaped as `"0`.) By default this feature is on.

    Note that valid Unicode (UTF-8) is not considered binary.

- strict


        my $csv = Text::CSV.new(:strict);
                  $csv.strict(False);
        my $flg = $csv.strict;

    If set to True, any row that parses to a different number of columns than
    the previous row will cause the parser to throw error 2014.

- formula-handling

- formula\_handling

- formula


        my $csv = Text::CSV.new(formula => "none");
                $csv.formula("none");
        my $f = $csv.formula;

    This defines the behavior of fields containing _formulas_. As formulas are
    considered dangerous in spreadsheets, this attribute can define an optional
    action to be taken if a field starts with an equal sign (`=`).

    For purpose of code-readability, this can also be written as

        my $csv = Text::CSV_XS.new(formula-handling => "none"});
                $csv.formula-handling("none");
        my $f = $csv.formula-handling;

    or

        my $csv = Text::CSV_XS.new(formula_handling => "none"});
                $csv.formula_handling("none");
        my $f = $csv.formula_handling;

    Possible values for this attribute are

    - none

        Take no specific action. This is the default.

            $csv.formula("none");

    - die

        Cause the process to `die` whenever a leading `=` is encountered.

            $csv.formula("die");

    - croak

        Cause the process to `die` whenever a leading `=` is encountered.

            $csv.formula("croak");

        This option just exists for perl5 compatibility.

    - diag

        Report position and content of the field whenever a leading `=` is found.
        The value of the field is unchanged.

            $csv.formula("diag");

    - empty

        Replace the content of fields that start with a `=` with the empty string.

            $csv.formula("empty");
            $csv.formula("");

    - undef

        Replace the content of fields that start with a `=` with `Str`.

            $csv.formula("undef");
            $csv.formula(Str);

    All other values will throw an exception with error code 1500.

- auto\_diag

- auto-diag


        my $csv = Text::CSV.new(auto_diag => False);
                  $csv.auto_diag(True);
        my $a-d = $csv.auto_diag;

    Set this attribute to a number between `1` and `9` causes ["error\_diag"](#error_diag)
    to be automatically called in void context upon errors. The value `True`
    evaluates to `1`.

    In case of error `2012 - EOF`, this call will be void.

    If `auto_diag` is set to a numeric value greater than `1`, it will `die`
    on errors instead of `warn`.

- diag\_verbose

- diag-verbose


        my $csv = Text::CSV.new(diag_verbose => 1);
                  $csv.diag_verbose(2);
        my $d-v = $csv.diag_verbose;

    Set the verbosity of the output triggered by `auto_diag`.

    WIP: Does not add any information yet.

- blank\_is\_undef

- blank-is-undef


        my $csv = Text::CSV.new(:blank_is_undef);
                  $csv.blank_is_undef(False);
        my $biu = $csv.blank_is_undef;

    Under normal circumstances, `CSV` data makes no distinction between quoted-
    and unquoted empty fields. These both end up in an empty string field once
    read, thus

        1,"",," ",2

    is read as

        [ "1", "", "", " ", "2" ]

    When _writing_ `CSV` files with [`always_quote`](#always_quote) set, the
    unquoted _empty_ field is the result of an undefined value. To enable this
    distinction when _reading_ `CSV` data, the `blank_is_undef` attribute
    will cause unquoted empty fields to be set to `Str`, causing the above
    to be parsed as

        [ "1", "", Str, " ", "2" ]

- empty\_is\_undef

- empty-is-undef


        my $csv = Text::CSV.new(:empty_is_undef);
                  $csv.empty_is_undef(False);
        my $eiu = $csv.empty_is_undef;

    Going one step further than [`blank_is_undef`](#blank_is_undef), this
    attribute causes all empty fields to return as `Str`, so

        1,"",," ",2

    is read as

        [ 1, Str, Str, " ", 2 ]

    Note that this effects only fields that are originally empty, not fields
    that are empty after stripping allowed whitespace. YMMV.

- allow\_whitespace

- allow-whitespace


        my $csv = Text::CSV.new(:allow_whitespace);
                  $csv.allow_whitespace(False);
        my $a-w = $csv.allow_whitespace;

    When this option is set to `True`, the whitespace (`TAB`'s and `SPACE`'s)
    surrounding the separation sequence is removed when parsing. If either
    `TAB` or `SPACE` is one of the three major sequences [`sep`](#sep),
    [`quote`](#quote), or [`escape`](#escape) it will not be considered
    whitespace.

    Now lines like:

        1 , "foo" , bar , 3 , zapp

    are parsed as valid `CSV`, even though it violates the `CSV` specs.

    Note that **all** whitespace is stripped from both start and end of each
    field. That would make it _more_ than a _feature_ to enable parsing bad
    `CSV` lines, as

        1,   2.0,  3,   ape  , monkey

    will now be parsed as

        [ "1", "2.0", "3", "ape", "monkey" ]

    even if the original line was perfectly acceptable `CSV`.

- allow\_loose\_quotes

- allow-loose-quotes


        my $csv = Text::CSV.new(:allow_loose_quotes);
                  $csv.allow_loose_quotes(False);
        my $alq = $csv.allow_loose_quotes;

    By default, parsing unquoted fields containing [`quote`](#quote)'s like

        1,foo "bar" baz,42

    would result in parse error 2034. Though it is still bad practice to allow
    this format, we cannot help the fact that some vendors make their
    applications spit out lines styled this way.

    If there is **really** bad `CSV` data, like

        1,"foo "bar" baz",42

    or

        1,""foo bar baz"",42

    there is a way to get this data-line parsed and leave the quotes inside the
    quoted field as-is. This can be achieved by setting `allow_loose_quotes`
    **AND** making sure that the [`escape`](#escape) is _not_ equal to
    [`quote`](#quote).

- allow\_loose\_escapes

- allow-loose-escapes


        my $csv = Text::CSV.new(:allow_loose_escapes);
                  $csv.allow_loose_escapes(False);
        my $ale = $csv.allow_loose_escapes;

    Parsing fields that have [`escape`](#escape) sequences that escape
    characters that do not need to be escaped, like:

        my $csv = Text::CSV.new(escape_char => "\\");
        $csv.parse(q{1,"my bar\'s",baz,42});

    would result in parse returning `False` with reason 2025. Though it is bad
    practice to allow this format, this attribute enables you to treat all
    escape sequences equal.

- allow\_unquoted\_escape

- allow-unquoted-escape


        my $csv = Text::CSV.new(:allow_unquoted_escape);
                  $csv.allow_unquoted_escape(False);
        my $aue = $csv.allow_unquoted_escape;

    A backward compatibility issue where [`escape`](#escape) differs from
    [`quote`](#quote) prevents [`escape`](#escape) to be in the first
    position of a field. If [`quote`](#quote) is equal to the default `"` and
    [`escape`](#escape) is set to `\`, this would be illegal:

        1,\0,2

    Setting this attribute to `True` might help to overcome issues with
    backward compatibility and allow this style.

- always\_quote

- always-quote


        my $csv = Text::CSV.new(:always_quote);
                $csv.always_quote(False);
        my $f = $csv.always_quote;

    By default the generated fields are quoted only if they _need_ to be. For
    example, if they contain the separator sequence. If you set this attribute
    to `1` then _all_ defined fields will be quoted. (undefined (`Str`)
    fields are not quoted, see ["blank\_is\_undef"](#blank_is_undef)). This makes it quite often
    easier to handle exported data in external applications. (Poor creatures
    who are better to use Text::CSV. :)

- quote\_empty

- quote-empty


        my $csv = Text::CSV.new(:quote_empty);
                  $csv.quote_empty(False);
        my $q-s = $csv.quote_empty;

    By default the generated fields are quoted only if they _need_ to be. An
    empty defined field does not need quotation. If you set this attribute to
    `True` then empty defined fields will be quoted. See also
    [`always_quote`](#always_quote).

- quote\_space

- quote-space


        my $csv = Text::CSV.new(:quote_space);
                  $csv.quote_space(False);
        my $q-s = $csv.quote_space;

    By default, a space in a field would trigger quotation. As no rule exists
    this to be forced in `CSV`, nor any for the opposite, the default is
    `True` for safety. You can exclude the space from this trigger by setting
    this attribute to `False`.

- escape\_null

- quote-null


        my $csv = Text::CSV.new(:escape_null);
                  $csv.escape_null(False);
        my $q-n = $csv.escape_null;

    By default, a `NULL` byte in a field would be escaped. This option enables
    you to treat the `NULL` byte as a simple binary character in binary mode
    (the `binary => True` is set). The default is `True`. You can prevent
    `NULL` escapes by setting this attribute to `False`.

- quote\_binary

- quote-binary


        my $csv = Text::CSV.new(:quote_binary);
                  $csv.quote_binary(False);
        my $q-b = $csv.quote_binary;

    By default, all "unsafe" bytes inside a string cause the combined field to
    be quoted. By setting this attribute to `False`, you can disable that
    trigger for bytes >= `0x7F`. (WIP)

- keep\_meta

- keep-meta


        my $csv = Text::CSV.new(:keep_meta);
                  $csv.keep_meta(False);
        my $k-m = $csv.keep_meta_info;

    By default, the parsing of input records is as simple and fast as possible.
    However, some parsing information - like quotation of the original field -
    is lost in that process. Setting this flag to true enables retrieving that
    information after parsing with the methods ["meta\_info"](#meta_info), ["is\_quoted"](#is_quoted),
    and ["is\_binary"](#is_binary) described below. Default is `False` for ease of use.

    If `keep-meta` is set to `True`, the returned fields are not of type
    `Str` but of type [`CSV::Field`](#csv-field).

- undef\_str

- undef-str


    NYI - this should replace undefined values in generating CSV

- comment\_str

- comment-str


        my $csv = Text::CSV.new(comment_str => "#");
                $csv.comment_str (Str);
        my $s = $csv.comment_str;

    This attribute optionally defines a string to be recognized as comment. If
    this attribute is defined, all lines starting with this sequence will not
    be parsed as CSV but skipped as comment.

    This attribute has no meaning when generating CSV.

- types

    NYI

    A set of column types; the attribute is immediately passed to the ["types"](#types)
    method.

- callbacks


    See the ["Callbacks"](#callbacks) section below.

To sum it up,

    $csv = Text::CSV.new;

is equivalent to

    $csv = Text::CSV.new(
        eol                   => Nil, # \r, \n, or \r\n
        sep                   => ',',
        quote                 => '"',
        escape                => '"',
        binary                => True,
        auto-diag             => False,
        diag-verbose          => 0,
        blank-is-undef        => False,
        empty-is-undef        => False,
        allow-whitespace      => False,
        allow-loose-quotes    => False,
        allow-loose-escapes   => False,
        allow-unquoted-escape => False,
        always-quote          => False,
        quote-space           => True,
        escape-null           => True,
        quote-binary          => True,
        keep-meta             => False,
        strict                => False,
        formula               => "none",
        undef-str             => Str,
        comment-str           => Str,
        types                 => Nil,
        callbacks             => Nil,
        });

For all of the above mentioned flags, an accessor method is available where
you can inquire the current value, or change the value

    my $quote = $csv.quote;
    $csv.binary(True);

It is not wise to change these settings halfway through writing `CSV` data
to a stream. If however you want to create a new stream using the available
`CSV` object, there is no harm in changing them.

If the ["new"](#new) constructor call fails, an exception of type `CSV::Diac` is
thrown with the reason like the ["error\_diag"](#error_diag) method would return:

    my $e;
    {   $csv = Text::CSV.new(ecs_char => ":") or
        CATCH { default { $e = $_; }}
        }
    $e and $e.message.say;

The message will be a string like

    "INI - Unknown attribute 'ecs_char'"

## print


    $status = $csv.print($io,  $fld, ... );
    $status = $csv.print($io, ($fld, ...));
    $status = $csv.print($io, [$fld, ...]);
    $status = $csv.print($io,  @fld      );

    $csv.column_names(%fld.keys); # or use a subset
    $status = $csv.print($io,  %fld      );

Similar to ["combine"](#combine) + ["string"](#string) + ["print"](#print), but much more efficient.
It takes an IO object and any number of arguments interpreted as fields.
The resulting string is immediately written to the `$io` stream.

NYI: no fields in combination with ["bind\_columns"](#bind_columns), like

    $csv.bind_columns(\($foo, $bar));
    $status = $csv.print($fh);

A benchmark showed this order of preference, but the difference is within
noise range:

    my @data = ^20;
    $csv.print($io,   @data  );   # 2.6 sec
    $csv.print($io, [ @data ]);   # 2.7 sec
    $csv.print($io,    ^20   );   # 2.7 sec
    $csv.print($io,  \@data  );   # 2.8 sec

## say


Is the same a s["print"](#print) where [`eol`](#eol) defaults to `$*OUT.nl`.

    $status = $csv.say($io,  $fld, ... );
    $status = $csv.say($io, ($fld, ...));
    $status = $csv.say($io, [$fld, ...]);
    $status = $csv.say($io,  @fld      );

    $csv.column_names(%fld.keys); # or use a subset
    $status = $csv.say($io,  %fld      );

## combine


    $status = $csv.combine(@fields);
    $status = $csv.combine($fld, ...);
    $status = $csv.combine(\@fields);

This method constructs a `CSV` row from `@fields`, returning success or
failure. Failure can result from lack of arguments or an argument that
contains invalid data. Upon success, ["string"](#string) can be called to retrieve
the resultant `CSV` string. Upon failure, the value returned by ["string"](#string)
is undefined and ["error\_input"](#error_input) could be called to retrieve the invalid
argument. (WIP)

## string


    $line = $csv.string;

This method returns the input to ["parse"](#parse) or the resultant `CSV` string
of ["combine"](#combine), whichever was called more recently. If [`eol`](#eol) is
defined, it is added to the string.

## getline


    @row = $csv.getline($io);
    @row = $csv.getline($io,  :meta);
    @row = $csv.getline($str);
    @row = $csv.getline($str, :meta);

This is the counterpart to ["print"](#print), as ["parse"](#parse) is the counterpart to
["combine"](#combine): it parses a row from the `$io` handle or `$str` using the
["getline"](#getline) method associated with `$io` (or the internal temporary IO
handle used to read from the string as if it were an IO handle) and parses
this row into an array. This array is returned by the function or `Array`
for failure. When `$io` does not support `getline`, you are likely to hit
errors.

NYI: When fields are bound with ["bind\_columns"](#bind_columns) the return value is a
reference to an empty list.

## getline\_all
=head2 getline-all



    @rows = $csv.getline_all($io);
    @rows = $csv.getline_all($io,                   :meta);
    @rows = $csv.getline_all($io, $offset);
    @rows = $csv.getline_all($io, $offset,          :meta);
    @rows = $csv.getline_all($io, $offset, $length);
    @rows = $csv.getline_all($io, $offset, $length, :meta);

This will return a list of [getline($io)](#getline) results.
If `$offset` is negative, as with `splice`, only the last `abs($offset)`
records of `$io` are taken into consideration.

Given a CSV file with 10 lines:

    lines call
    ----- ---------------------------------------------------------
    0..9  $csv.getline_all($io)         # all
    0..9  $csv.getline_all($io,  0)     # all
    8..9  $csv.getline_all($io,  8)     # start at 8
    -     $csv.getline_all($io,  0,  0) # start at 0 first 0 rows
    0..4  $csv.getline_all($io,  0,  5) # start at 0 first 5 rows
    4..5  $csv.getline_all($io,  4,  2) # start at 4 first 2 rows
    8..9  $csv.getline_all($io, -2)     # last 2 rows
    6..7  $csv.getline_all($io, -4,  2) # first 2 of last  4 rows

## getline\_hr
=head2 getline-hr



The ["getline\_hr"](#getline_hr) and ["column\_names"](#column_names) methods work together to allow you
to have rows returned as hashes instead of arrays. You must invoke
["column\_names"](#column_names) first to declare your column names.

    $csv.column_names(< code name price description >);
    %hr = $csv.getline_hr($str, :meta);
    %hr = $csv.getline_hr($io);
    say "Price for %hr<name> is %hr<price> \c[EURO SIGN]";

["getline\_hr"](#getline_hr) will fail if invoked before ["column\_names"](#column_names).

## getline\_hr\_all
=head2 getline-hr-all



    @rows = $csv.getline_hr_all($io);
    @rows = $csv.getline_hr_all($io,                   :meta);
    @rows = $csv.getline_hr_all($io, $offset);
    @rows = $csv.getline_hr_all($io, $offset,          :meta);
    @rows = $csv.getline_hr_all($io, $offset, $length);
    @rows = $csv.getline_hr_all($io, $offset, $length, :meta);

This will return a list of [getline\_hr($io)](#getline_hr) results.

## parse


    $status = $csv.parse($line);

This method decomposes a `CSV` string into fields, returning success or
failure. Failure can result from a lack of argument or improper format in
the given `CSV` string. Upon success, invoke ["fields"](#fields) or ["strings"](#strings) to
get the decomposed fields. Upon failure these methods shall not be trusted
to return reliable data.

NYI: You may use the ["types"](#types) method for setting column types. See
["types"](#types)' description below.

## fragment


This function implements [RFC7111](https://datatracker.ietf.org/doc/html/rfc7111)
(URI Fragment Identifiers for the text/csv Media Type).

    my @rows = $csv.fragment($io, $spec);

In specifications, `*` is used to specify the _last_ item, a dash (`-`)
to indicate a range. All indices are `1`-based: the first row or column
has index `1`. Selections can be combined with the semi-colon (`;`).

When using this method in combination with ["column\_names"](#column_names), the returned
array will be a list of hashes instead of an array of arrays. A disjointed
cell-based combined selection might return rows with different number of
columns making the use of hashes unpredictable.

    $csv.column_names(< Name Age >);
    my @rows = $csv.fragment($io, "row=3;8");

Note that for `col=".."`, the column names are the names for _before_ the
selection is taken to make it more consistent with reading possible headers
from the first line of the CSV datastream.

    $csv,column_names(< foo bar >);  # WRONG
    $csv.fragment($io, "col=3");

would set the column names for the first two columns that are then skipped
in the fragment. To skip the unwanted columns, use placeholders.

    $csv.column_names(< x x Name >);
    $csv.fragment($io, "col=3");

WIP: If the ["after\_parse"](#after_parse) callback is active, it is also called on every
line parsed and skipped before the fragment.

- row

        row=4
        row=5-7
        row=6-*
        row=1-2;4;6-*

- col

        col=2
        col=1-3
        col=4-*
        col=1-2;4;7-*

- cell

    In cell-based selection, the comma (`,`) is used to pair row and column

        cell=4,1

    The range operator (`-`) using `cell`s can be used to define top-left and
    bottom-right `cell` location

        cell=3,1-4,6

    The `*` is only allowed in the second part of a pair

        cell=3,2-*,2    # row 3 till end, only column 2
        cell=3,2-3,*    # column 2 till end, only row 3
        cell=3,2-*,*    # strip row 1 and 2, and column 1

    Cells and cell ranges may be combined with `;`, possibly resulting in rows
    with different number of columns

        cell=1,1-2,2;3,3-4,4;1,4;4,1

    Disjointed selections will only return selected cells. The cells that are
    not specified will not be included in the returned set, not even as
    `Str`. As an example given a `CSV` like

        11,12,13,...19
        21,22,...28,29
        :            :
        91,...97,98,99

    with `cell=1,1-2,2;3,3-4,4;1,4;4,1` will return:

        11,12,14
        21,22
        33,34
        41,43,44

    Overlapping cell-specs will return those cells only once, So
    `cell=1,1-3,3;2,2-4,4;2,3;4,2` will return:

        11,12,13
        21,22,23,24
        31,32,33,34
        42,43,44

[RFC7111](https://datatracker.ietf.org/doc/html/rfc7111) does **not** allow different
types of specs to be combined (either `row` _or_ `col` _or_ `cell`).
Passing an invalid fragment specification will croak and set error 2013.

Using ["colrange"](#colrange) and ["rowrange"](#rowrange) instead of ["fragment"](#fragment) will allow you
to combine row- and column selecting as a grid.

## colrange


    my Int @range = ^5, 5..9;
    $csv.colrange(@range);
    $csv.colrange("0-4;6-10");
    my @range = $csv.colrange;

Set or inspect the column ranges. When passed as an array of `Int`, the
indexes are 0-based. When