How to generate CSV from array of hashes in Perl?
Question
How to generate CSV from array of hashes in Perl?
Generating CSV output from an array of hashes is a common task in Perl, especially when exporting data for spreadsheets or reports. Perl offers flexible ways to handle this, ranging from manual string manipulation to using built-in or core modules designed for CSV processing.
Understanding the Data Structure
An array of hashes is one of Perl's popular data structures for tabular data. Each hash represents a row, with keys as column names and values as the cell values. For example:
my @data = (
{ name => "Alice", age => 30, city => "London" },
{ name => "Bob", age => 25, city => "Paris" },
{ name => "Carol", age => 27, city => "New York" },
);
Key Points When Generating CSV
- Consistent Columns: You usually want consistent columns for each row. This means choosing a fixed set of keys/fields.
- Quoting & escaping: CSV fields sometimes need quotes, especially if they contain commas, quotes, or newlines.
- Header Row: The CSV usually starts with a header line listing column names.
- Perl Context and Sigils: Use appropriate sigils to access scalars (
$hash{key}) and arrays (@array).
Manual CSV Generation Example
This example demonstrates a simple way to generate CSV without external modules, handling minimal quoting:
use strict;
use warnings;
my @data = (
{ name => "Alice", age => 30, city => "London" },
{ name => "Bob", age => 25, city => "Paris" },
{ name => "Carol", age => 27, city => "New York" },
);
# Define columns explicitly to ensure order & presence
my @columns = qw(name age city);
# Print CSV header
print join(",", @columns), "\n";
# Print each row
foreach my $row (@data) {
my @fields = map {
my $val = defined $row->{$_} ? $row->{$_} : "";
# Simple quoting if needed
if ($val =~ /[",\n]/) {
$val =~ s/"/""/g; # Escape double quotes by doubling
$val = qq{"$val"}; # Surround by double quotes
}
$val
} @columns;
print join(",", @fields), "\n";
}
This code:
- Fixes the column order with
@columnsto avoid Perl's unordered hash keys. - Escapes any embedded quotes by doubling them, per CSV spec.
- Wraps fields containing commas, quotes, or newlines inside quotes.
- Prints directly to STDOUT for easy redirection or capture.
Using Core Module Text::CSV (Recommended for Robustness)
Perl core includes Text::CSV from version 5.8 (bundled with Perl), which handles CSV specifics like Unicode, escaping, separators, and other edge cases reliably.
use strict;
use warnings;
use Text::CSV;
my @data = (
{ name => "Alice", age => 30, city => "London" },
{ name => "Bob, Jr.", age => 25, city => "Paris" },
{ name => "Carol \"CJ\"", age => 27, city => "New York" },
);
my @columns = qw(name age city);
my $csv = Text::CSV->new({ binary => 1, eol => "\n" });
# Print header
$csv->print(*STDOUT, \@columns);
# Print rows
for my $row (@data) {
my @fields = map { $row->{$_} // "" } @columns;
$csv->print(*STDOUT, \@fields);
}
Text::CSV gets rid of manual quoting headaches and handles tricky fields (quotes, commas, newlines) properly.
Common Gotchas
- Hash Key Ordering: Hash keys in Perl are unordered, so you should explicitly specify column order.
- Missing Data: Handle undefined or missing values, e.g. with the defined-or operator
//to prevent warnings. - Unicode & Binary: Use
Text::CSV->new({ binary => 1 })when dealing with UTF-8 or binary data. - Never trust manual concatenation for CSV in complex cases; prefer
Text::CSV.
Summary
You can generate CSV from an array of hashes in Perl either by:
- Writing your own printer with correct quoting and fixed columns, or
- Using the
Text::CSVcore module for robust and easy CSV handling.
Text::CSV is the best choice.Verified Code
Executed in a sandbox to capture real output. • v5.34.1 • 8ms
(empty)(empty)