Here's a quick subclass of
Text::CSV_XS to return you an opaque $quote object from getline() that you pass to print(). Two gotchas though: First, commas embedded in a field screw it up. Second, I only implemented new, getline(), and print(); not combine() or parse() or anything else.
#!/usr/bin/perl
use strict;
use warnings;
{
package Text::CSVPreserveQuotes;
use base 'Text::CSV_XS';
sub new
{
my ($class) = @_;
my $self = $class->SUPER::new
({ quote_char => undef,
eol => "\n",
escape_char => undef,
sep_char => ",",
});
bless $self, $class;
}
# my ($row, $quote) = $csv->getline($io);
#
# $quote is opaque; pass it to print()
#
sub getline
{
my ($self, $io) = @_;
my $row = $self->SUPER::getline($io);
return if not defined $row;
my $quote = [];
for (@$row)
{
push @$quote, s[^"(.*)"$][$1]s ? 1 : 0;
}
return ($row, $quote);
}
# $csv->print($io, $row, $quote);
#
# $quote is what you got from ->getline()
#
# $row and $quote will get mangled!
#
sub print
{
my ($self, $io, $row, $quote) = @_;
for (@$row)
{
$_ = qq{"$_"} if shift @$quote;
}
$self->SUPER::print($io, $row);
}
}
use IO::Wrap qw( wraphandle );
my $data = wraphandle(\*DATA);
my $stdout = wraphandle(\*STDOUT);
my $csv = Text::CSVPreserveQuotes->new();
while (1)
{
my ($row, $quote) = $csv->getline($data);
die if not defined $row;
last if @$row == 0;
# Munge
#
for (@$row)
{
if (/^[\d.]+$/) { $_ += 42 }
else { tr/A-Za-zm/N-ZA-Mn-za-m/ }
$_ = "<$_>";
}
$csv->print($stdout, $row, $quote);
}
__DATA__
"hi",3,20.6,"green","32"
16,"alpha",0.00
"bye",3,27.6,"green","32"
16,"beta",0.00
"This won't work, since it has an embedded comma"
If the strings can have commas, I don't know if Text::CSV_XS can help you.