workInProgress12 has asked for the wisdom of the Perl Monks concerning the following question:

Hey all, trying to make a comprehensive perl function that can be used in various flows for my work. The stuff that I'm struggling with is:

$outputType – if left blank (‘’) assume array reference (‘aref’), only other possibility is hash reference (‘href’)

$headingAref – if left blank (‘’) assume first line as the header. Otherwise pass in an array reference where each element in the array is the column heading in the order they appear in the file.

$readLineFrom – if left blank (‘’) start at the first line, otherwise if a number defined start reading at that line of the file.

$readLineTo – if left blank (‘’) read till EOF, otherwise if a number defined end reading at that line of the file.

Trying to read the csv from a certain row to another is proving to be difficult. I tried to keep an index and use a nested for loop but this is still printing all my rows, not just the ones I want. Furthermore, I need to be able to add my own personal header according to $headerAref. I tried opening the file for reading and writing using "+<" but that causes my code to only print the header. I also need to duplicate all of this for a hash output type as well but I have not yet looked into this. Right now, my code is able to output the csv file as an array or arrays as requested but reads each row (not the range given) and can't add a header. Any help would be appreciated.

#!/tool/pandora64/bin/perl5.8.8 #MODULES use strict; use warnings; use Pod::Usage; use Data::Dumper; use Getopt::Long; use File::Basename; use Cwd 'abs_path'; use Data::Dump 'dd'; use Data::Dumper qw(Dumper); use Text::CSV; #my $output = readCSV($pathToCSV, $columnSeperatorChar, $cellEncapsual +tingChar, $outputType, $headingAref, $readLineFrom, $readLineTo, $ign +oreFirstLineBool); my @headerArray = "a,b,c,d,e,f,g,h,i,j,k"; my $headerArrayRef = \@headerArray; my $pathToCSV = "input.csv"; my $columnSeperatorChar = ""; my $cellEncapsulatingChar = ""; my $outputType = ""; my $headingAref = "$headerArrayRef"; my $readLineFrom = 8; my $readLineTo = ""; my $ignoreFirstLineBool = 0; $columnSeperatorChar = "," if ($columnSeperatorChar eq ""); $cellEncapsulatingChar = "" if ($cellEncapsulatingChar eq ""); $outputType = 'aref' if ($outputType eq ""); my @output; my $rowCounter = 0; my @index; my $currentIndex = 0; open(my $file, "<", $pathToCSV) or die "File could not be opened $!\n" +; =begin comment if($headingAref ne ""){ my @headerArrayCopy = @$headingAref; print ($file "@headerArrayCopy\n"); #adds header } =end comment =cut while (my $line = <$file>){ chomp $line; $currentIndex++; if ($readLineTo eq ""){ $rowCounter++; $readLineTo = $rowCounter; } for ($currentIndex >= $readLineFrom and $currentIndex <= $readLine +To){ my @row = split ("$columnSeperatorChar", $line); foreach my $cell (@row){ $cell = "$cellEncapsulatingChar$cell$cellEncapsulatingChar +" } push @output, \@row; } } print Dumper(\@output);

Replies are listed 'Best First'.
Re: Reading CSV Function
by Tux (Canon) on Jun 15, 2021 at 07:31 UTC

    Text::CSV_XS' csv function supports most (if not all) of what you want.


    Enjoy, Have FUN! H.Merijn
Re: Reading CSV Function
by swl (Priest) on Jun 15, 2021 at 03:08 UTC

    Just a general piece of advice, but it's best to avoid hand-rolling your own CSV parser. Use Text::CSV instead, preferably with Text::CSV_XS also installed so you get the speed boost.

    As for handling arguments, you can use Boolean assignment in many cases. For example this

    $columnSeperatorChar = "," if ($columnSeperatorChar eq "");

    can be written as

    $columnSeperatorChar ||= ",";

    (assuming you will never use a 0 as a column separator)

Re: Reading CSV Function
by jwkrahn (Monsignor) on Jun 15, 2021 at 03:39 UTC
    my @headerArray = "a,b,c,d,e,f,g,h,i,j,k"; my $headerArrayRef = \@headerArray; ... my $headingAref = "$headerArrayRef"; ... my @headerArrayCopy = @$headingAref; print ($file "@headerArrayCopy\n"); #adds header

    @headerArray starts out as a single string (an array of one element).

    You then assign a reference of that array to $headerArrayRef, which could have been done in one step like this:

    my $headerArrayRef = [ "a,b,c,d,e,f,g,h,i,j,k" ];

    Next you convert that reference to a string and assign it to $headingAref. Which means that $headingAref can never access the original data from @headerArray.

    And finally you dereference a string which is an error and will not work.

    for ($currentIndex >= $readLineFrom and $currentIndex <= $readLine +To){

    In Perl a for (foreach) loop iterates over a list but that is a list of one value (whatever $currentIndex >= $readLineFrom and $currentIndex <= $readLineTo evaluates to).

    It looks like that should be if instead of for:

    if ($currentIndex >= $readLineFrom and $currentIndex <= $readLineT +o){

    Or you could do it the "Perl" way:

    if ( $readLineFrom .. $readLineTo ) {