1

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
4
  • I tried your code and it printed aa 1 8 bb 2 10 cc 3 12 dd 4 13. Commented Aug 12, 2020 at 6:36
  • 2
    You might be interested in array slices to simplify your code: push @d, @list[0,1,4]; or even push @d, (split /,/)[0,1,4]; Commented Aug 12, 2020 at 6:52
  • 5
    As for only seeing a single row... I bet your CSV file has Windows style CRLF endings and you're running this on an OS with LF line endings. Try open FH, "<:crlf", $fh or running the file through dos2unix or s/\r?\n//; instead of chomp. Commented Aug 12, 2020 at 6:56
  • What is your expected output exactly? Commented Aug 12, 2020 at 9:13

2 Answers 2

2

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.

Sign up to request clarification or add additional context in comments.

Comments

1

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.