Reputation: 21
I am trying to get some elements of array to return in the subroutine .it gives me only the last one dd 4 13
here is my csv
aa ,1 ,no ,ed ,8
bb ,2 ,yes ,ed ,10
cc ,3 ,no ,ed ,12
dd ,4 ,yes ,ed ,13
here is my perl code
use strict;
use Getopt::Std;
my $input_file = $ARGV[0];
my @data = read_data_from_csv($input_file);
sub read_data_from_csv
{
my ($fh) = @_;
my @d = ();
open(FH, "<$fh") || die "Error: no open for $fh: $!";
while (<FH>) {
chomp;
my @list =split(/,/);
my ($aa) = $list[0];
my ($bb) = $list[1];
my ($cc) = $list[4];
push (@d , ($aa, $bb, $cc));
}
close (FH);
return @d
}
print "@data\n";
cat test.csv
aa ,1 ,no ,ed ,8
bb ,2 ,yes ,ed ,10
cc ,3 ,no ,ed ,12
dd ,4 ,yes ,ed ,13
/pkg/qct/software/perl/5.20.0/bin/perl test.pl test.csv
dd yes 13
Upvotes: 1
Views: 83
Reputation: 482
You can use https://metacpan.org/pod/Text::CSV_XS module. Text::CSV_XS - comma-separated values manipulation routines. you can use functionality as per your requirement.
use strict;
use warnings;
use Text::CSV_XS;
use Data::Dumper qw(Dumper);
my @rows;
my @column_numbers = (0,1,4);
my $csv_file = "tst.csv";
# check https://metacpan.org/pod/Text::CSV_XS#binary
# check http://metacpan.org/pod/Text::CSV_XS#auto_diag
my $csv = Text::CSV_XS->new ({ binary => 1, auto_diag => 1 });
open my $fh, "<:encoding(utf8)", $csv_file or die "$csv_file: $!";
# $csv->getline_all ($fh); will return a reference to a list of getline ($fh) results.
#The map function of Perl provides a simple way to transform a list of values to another list of values.
@rows = map { [ @{ $_ }[@column_numbers] ] } @{ $csv->getline_all($fh) };
print Dumper(\@rows);
close $fh;
Output
$VAR1 = [
[
'aa ',
'1 ',
'8 '
],
[
'bb ',
'2 ',
'10'
],
[
'cc ',
'3 ',
'12'
],
[
'dd ',
'4 ',
'13'
]
];
Note : Please check solution provided by @ikegami Unable to read multiple columns from a .csv using Text::CSV_XS in Perl
map: 1) https://perldoc.perl.org/functions/map.html 2) https://perlmaven.com/transforming-a-perl-array-using-map
Upvotes: 1
Reputation: 53478
Your code as posted works:
use strict;
use Getopt::Std;
my $input_file = $ARGV[0];
my @data = read_data_from_csv($input_file);
sub read_data_from_csv {
my ($fh) = @_;
my @d = ();
#open(FH, "<$fh") || die "Error: no open for $fh: $!";
while (<DATA>) {
chomp;
my @list = split(/,/);
my ($aa) = $list[0];
my ($bb) = $list[1];
my ($cc) = $list[4];
push( @d, ( $aa, $bb, $cc ) );
}
close(FH);
return @d;
}
print "@data\n";
__DATA__
aa ,1 ,no ,ed ,8
bb ,2 ,yes ,ed ,10
cc ,3 ,no ,ed ,12
dd ,4 ,yes ,ed ,13
Output:
aa 1 8 bb 2 10 cc 3 12 dd 4 13
I would suggest you need to check your input file - if on unix, try cat -v
which might show you you've got bad line endings.
An easy fix if you do have this problem (or to test it) is include:
s/[\r\n]//g;
More generally though I think there's a few errors in your code - that 'push' for example, probably isn't doing what you're thinking, because you're compressing your CSV into a flat array.
I'd also suggest using Data::Dumper
to test outputs, because it's clearer what's happening:
$VAR1 = [
' aa ',
'1 ',
'8 ',
' bb ',
'2 ',
'10',
' cc ',
'3 ',
'12',
'dd ',
'4 ',
'13'
];
As you can see, you've flattened your data, which I am assuming isn't what you want, based on what your write in push
.
So you might want to consider using []
instead, because then you get:
$VAR1 = [
[
' aa ',
'1 ',
'8 '
],
[
' bb ',
'2 ',
'10'
],
[
' cc ',
'3 ',
'12'
],
[
'dd ',
'4 ',
'13'
]
];
You can also direct assign array slices, rather than having a 1-1:
push ( @d, [ @list[0,1,4] ] );
Also as a final point - it's bad style to use single letter variables, and you should also use warnings;
.
Giving you:
use strict;
use Getopt::Std;
use warnings;
use Data::Dumper;
my $input_file = $ARGV[0];
my @data = read_data_from_csv($input_file);
sub read_data_from_csv {
my ($fh) = @_;
my @d = ();
##NB Commented out so I can use inline data
#open(FH, "<$fh") || die "Error: no open for $fh: $!";
while (<DATA>) {
chomp;
s/[\r\n]//g;
my @list = split(/,/);
push( @d, [ @list[0,1,4] ]);
}
##close(FH);
return @d;
}
print Dumper \@data;
__DATA__
aa ,1 ,no ,ed ,8
bb ,2 ,yes ,ed ,10
cc ,3 ,no ,ed ,12
dd ,4 ,yes ,ed ,13
You might want to consider that instead of opening the file specified in $ARGV[0]
this is precisely what the 'magic' file handle <>
does. It opens file specified on command line and iterates them, but it also allows you to 'pipe' into the process on STDIN
:
Making your program:
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
## flattened array
my @data_A = map { s/[\r\n]//g; ( split(/,/) ) [0,1,4] } <>;
## preserving structure
my @data_B = map { s/[\r\n]//g; [( split(/,/) ) [0,1,4]] } <>;
print Dumper \@data_B;
I appreciate your code is probably a reduction of the core problem, but I'm just wanting to illustrate simplification options.
Upvotes: 2