text-processing intermediate

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 @columns to 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::CSV core module for robust and easy CSV handling.
For most serious or production cases, Text::CSV is the best choice.

Verified Code

Executed in a sandbox to capture real output. • v5.34.1 • 8ms

Tip: edit code and use “Run (Browser)”. Server runs always execute the published, verified snippet.
STDOUT
(empty)
STDERR
(empty)

Was this helpful?

Related Questions