Jim
Jim

Reputation: 19582

Save a row to csv format

I have a set of rows from a DB that I would like to save to a csv file.
Taking into account that the data are ascii chars without any weird chars would the following suffice?

my $csv_row = join( ', ', @$row );  
# save csv_row to file    

My concern is if that would create rows that would be acceptable as CSV by any tool and e.g not be concern with quoting etc.

Update:
Is there any difference with this?

my $csv = Text::CSV->new ( { binary => 1, eol    => "\n"} );
my $header = join (',', qw( COL_NAME1 COL_NAME2 COL_NAME3 COL_NAME4 ) );
$csv->print( $fh, [$header] );                                                                             
foreach my $row ( @data ) {  
  $csv->print($fh,  $row );    
}    

This gives me as a first line:

" COL_NAME1,COL_NAME2,COL_NAME3,COL_NAME4"   

Please notice the double quotes and the rest of the rows are without any quotes.
What is the difference than my plain join? Also do I need the binary set?

Upvotes: 2

Views: 435

Answers (1)

zdim
zdim

Reputation: 66964

The safest way should be to write clean records with a comma separator. The simpler the better, specially with the format that has so much variation in real life. If needed, double quote each field.

The true strength in using the module is for reading of "real-life" data. But it makes perfect sense to use it for writing as well, for a uniform approach to CSV. Also, options can then be set in a clear way, and the module can iron out some glitches in data.

The Text::CSV documentation tells us about binary option

Important Note: The default behavior is to accept only ASCII characters in the range from 0x20 (space) to 0x7E (tilde). This means that the fields can not contain newlines. If your data contains newlines embedded in fields, or characters above 0x7E (tilde), or binary data, you must set binary => 1 in the call to new. To cover the widest range of parsing options, you will always want to set binary.

I'd say use it. Since you write a file this may be it for options, along with eol (or use say method). But do scan the many useful options and review their defaults.

As for your header, the print method expects an array reference where each field is an element, not a single string with comma-separated fields. So it is wrong to say

my $header = join (',', qw(COL_NAME1 COL_NAME2 COL_NAME3 COL_NAME4));  # WRONG
$csv->print( $fh, [$header] );

since the $header is a single string which is then made the sole element of the (anonymous) array reference created by [ ... ]. So it prints this string as the first field in the row, and since it detects in it the separator , itself it also double-quotes. Instead, you should have

$csv->print($fh, [COL_NAME1 COL_NAME2 COL_NAME3 COL_NAME4]);

or better assign column names to @header and then do $csv->print($fh, \@header).

This is also an example of why it is good to use the module for writing – if a comma slips into an element of the array, supposed to be a single field, it is handled correctly by double-quoting.


A complete example

use warnings;
use strict;
use Text::CSV_XS;

my $csv = Text::CSV->new ( { binary => 1, eol => "\n" } ) 
    or die "Cannot use CSV: " . Text::CSV->error_diag();

my $file = 'output.csv';
open my $fh_out , '>', 'output.csv' or die "Can't open $file for writing: $!";

my @headers = qw( COL_NAME1 COL_NAME2 COL_NAME3 COL_NAME4 );
my @data = 1..4;

$csv->print($fh_out, \@headers);
$csv->print($fh_out, \@data);

close $fh_out;

what produces the file output.csv

COL_NAME1,COL_NAME2,COL_NAME3,COL_NAME4
1,2,3,4

Upvotes: 2

Related Questions