File::Raw · File::Raw::Separated 0.04

File::Raw::Separated

CSV and TSV, parsed in C, as a File::Raw plugin.

A CSV or TSV file to rows in one call: arrays, or hashes keyed by the header. RFC 4180 by default, every dialect knob when the file needs one, and a stream for files larger than memory.

Registers csvtsv

use File::Raw ();
use File::Raw::Separated;

my $rows = File::Raw::slurp('people.csv', plugin => 'csv', header => 1);
print "$_->{name} is $_->{age}\n" for @$rows;

my $tabs = File::Raw::slurp('people.tsv', plugin => 'tsv');

What you get

  • csv and tsv

    Two plugins, the same options: RFC 4180 quoting by default, sep, quote, escape, trim and eol when a file differs.

  • Arrays or hashes

    Rows as arrays, or header => 1 for hashes keyed by the first line.

  • Lenient, or strict

    By default a malformed row is recovered. strict => 1 croaks with the byte offset instead, and the path when streaming.

  • Larger than memory

    file_parse_stream reads a file a row at a time, and hands the callback the same array each row so nothing is allocated per line.

  • In chains

    After gzip or base64 in a plugin chain, a compressed or encoded file is rows in one call.

In use

use File::Raw::Separated qw(import);

# Files larger than memory: one row at a time.
my $total = 0;
file_parse_stream('people.csv', sub {
    my $row = $_[0];        # reused between calls: copy it to keep it
    $total += $row->[1] if $row->[1] =~ /^\d+$/;
});
print "$total\n";

The numbers

Against the fastest alternative that was installed, and shown whichever way it went.

  • 3.4x read a 6.8 MB CSV file into rows, against Text::CSV_XS File::Raw::Separated 0.04 at 23.2/s, Text::CSV_XS 1.62 at 6.8/s
  • 2.7x read a 6.8 MB CSV file into hashes by its header, against Text::CSV_XS File::Raw::Separated 0.04 at 6.8/s, Text::CSV_XS 1.62 at 2.5/s

Measured on Apple M5, darwin with perl 5.42.0, 2026-10-19: each entry timed for 3 CPU seconds by the Benchmark module, 3 times, the median kept, after checking that both sides produced the same result. Every number.

Works with

Chains from its documentation: the plugins run left to right on a read, right to left on a write.

Get it

cpanm File::Raw::Separated

The reference

Every File::Raw dist