[Perl] Different printf formating for different print options

Hi,

Struggling with single quotes, double quotes, etc.
I want to print a header line, followed by lines with actual values, based on a print option.
In real life it is going to be something like 15 print options and 50 values.
Output will be 1 header and several value lines.
In this example I have just 3 print options and 5 values.
Output is just 1 header and one value line.

For some reason I cannot get it working.
I get uninitialized values and quotes where I do not want them.
It expects too many values (seen by the extra comma's in the output).
And therefore I could use some expert help.

Here the example.pl code to make it, hopefully, more clear.

#!/bin/perl -w #-d
use strict;

my $PrintOption = $ARGV[0] || 0;

my $Value1 = "one";
my $Value2 = "two";
my $Value3 = "three";
my $Value4 = "four";
my $Value5 = "five";

my $PrintFormat;
my $PrintFormat1 = q(%s);
my $PrintFormat23 = q(%s,%s);
my $PrintFormat45 = q(%s,%s);

my $PrintHeader;
my $PrintHeader1 = q("Value1");
my $PrintHeader23 = q("Value2","Value3");
my $PrintHeader45 = q("Value4","Value5");

my $PrintValues;
my $PrintValue1 = $Value1;
my $PrintValue23 = qq($Value2,$Value3);
my $PrintValue45 = qq($Value4,$Value5);

if ( $PrintOption eq 1 ) {
  $PrintFormat = "$PrintFormat1,$PrintFormat23";
  $PrintHeader = "$PrintHeader1,$PrintHeader23";
  $PrintValues = "$PrintValue1,$PrintValue23";
  printf "Expected Output with PrintOption %s\n\n", $PrintOption;
  printf "%s,%s,%s\n", "Value1","Value2","Value3";
  printf "%s,%s,%s\n", $Value1,$Value2,$Value3;
} elsif ( $PrintOption eq 2 ) {
  $PrintFormat = "$PrintFormat1,$PrintFormat45";
  $PrintHeader = "$PrintHeader1,$PrintHeader45";
  $PrintValues = "$PrintValue1,$PrintValue45";
  printf "Expected Output with PrintOption %s\n\n", $PrintOption;
  printf "%s,%s,%s\n", "Value1","Value4","Value5";
  printf "%s,%s,%s\n", $Value1,$Value4,$Value5;
} else {
  $PrintFormat = "$PrintFormat1,$PrintFormat23,$PrintFormat45";
  $PrintHeader = "$PrintHeader1,$PrintHeader23,$PrintHeader45";
  $PrintValues = "$PrintValue1,$PrintValue23,$PrintValue45";
  printf "Expected Output with PrintOption %s\n\n", $PrintOption;
  printf "%s,%s,%s,%s,%s\n", "Value1","Value2","Value3","Value4","Value5";
  printf "%s,%s,%s,%s,%s\n", $Value1,$Value2,$Value3,$Value4,$Value5;
}

printf "\nActual Output with PrintOption %s\n\n", $PrintOption;
printf $PrintFormat, $PrintHeader;
printf "\n";
printf $PrintFormat, $PrintValues;
printf "\n";

Result:

# ./example.pl 1
Expected Output with PrintOption 1

Value1,Value2,Value3
one,two,three

Actual Output with PrintOption 1

Use of uninitialized value in printf at ./example.pl line 51.
Use of uninitialized value in printf at ./example.pl line 51.
"Value1","Value2","Value3",,
Use of uninitialized value in printf at ./example.pl line 53.
Use of uninitialized value in printf at ./example.pl line 53.
one,two,three,,

---------- Post updated at 02:17 PM ---------- Previous update was at 10:08 AM ----------

When I use:

  printf $PrintFormat, "Value1","Value2","Value3";
  printf $PrintFormat, $Value1,$Value2,$Value3;

I get the right output:

Value1,Value2,Value3
one,two,three

With:

 printf $PrintFormat, $PrintHeader;
 printf $PrintFormat, $PrintValue23;

I get:

Use of uninitialized value in printf at ./ej.pl line 32.
Use of uninitialized value in printf at ./ej.pl line 32.
"Value1","Value2","Value3"
,,
Use of uninitialized value in printf at ./ej.pl line 34.
Use of uninitialized value in printf at ./ej.pl line 34.
two,three,,

Some debugging info:

  DB<1> x $PrintFormat
0  '%s,%s,%s
'
  DB<2> x $PrintHeader
0  '"Value1","Value2","Value3"
'
  DB<3> x $PrintValues
0  'one,two,three'

  DB<4> printf $PrintFormat, "1", "2", "3";
1,2,3

The 'problem' is that $PrintHeader is seen as 1 string for the printf command.
That is why I get 2 uninitialized values.

---------- Post updated at 03:19 PM ---------- Previous update was at 02:17 PM ----------

This works.
Perhaps not very prety, but it does the job.
Had to use arrays.
And using a string is a bit more easy than using a variable, when working with arrays.
But I am just an amateur.
Always open for suggestion on how to make it more simple.

#!/bin/perl -w #-d
use strict;

my $PrintOption = $ARGV[0] || 0;

my $Value1 = "one";
my $Value2 = "two";
my $Value3 = "three";
my $Value4 = "four";
my $Value5 = "five";

my $PrintFormat;
my $PrintFormat1 = q(%s);
my $PrintFormat23 = q(%s,%s);
my $PrintFormat45 = q(%s,%s);

my @PrintHeader;
my @PrintHeader1 = qw(Value1);
my @PrintHeader23 = qw(Value2 Value3);
my @PrintHeader45 = qw(Value4 Value5);

my @PrintValues;
my @PrintValue23;
my @PrintValue45;
my @PrintValue1 = qq($Value1);
push (@PrintValue23, $Value2, $Value3);
push (@PrintValue45, $Value4, $Value5);

if ( $PrintOption eq 1 ) {
  $PrintFormat = "$PrintFormat1,$PrintFormat23\n";
  @PrintHeader = (@PrintHeader1,@PrintHeader23);
  @PrintValues = (@PrintValue1,@PrintValue23);
  printf "Expected Output with PrintOption %s\n\n", $PrintOption;
  printf "%s,%s,%s\n", "Value1","Value2","Value3";
  printf "%s,%s,%s\n", $Value1,$Value2,$Value3;
} elsif ( $PrintOption eq 2 ) {
  $PrintFormat = "$PrintFormat1,$PrintFormat45\n";
  @PrintHeader = (@PrintHeader1,@PrintHeader45);
  @PrintValues = (@PrintValue1,@PrintValue45);
  printf "Expected Output with PrintOption %s\n\n", $PrintOption;
  printf "%s,%s,%s\n", "Value1","Value4","Value5";
  printf "%s,%s,%s\n", $Value1,$Value4,$Value5;
} else {
  $PrintFormat = "$PrintFormat1,$PrintFormat23,$PrintFormat45\n";
  @PrintHeader = (@PrintHeader1,@PrintHeader23,@PrintHeader45);
  @PrintValues = (@PrintValue1,@PrintValue23,@PrintValue45);
  printf "Expected Output with PrintOption %s\n\n", $PrintOption;
  printf "%s,%s,%s,%s,%s\n", "Value1","Value2","Value3","Value4","Value5";
  printf "%s,%s,%s,%s,%s\n", $Value1,$Value2,$Value3,$Value4,$Value5;
}

printf "\nActual Output with PrintOption %s\n\n", $PrintOption;
printf $PrintFormat, @PrintHeader;
printf $PrintFormat, @PrintValues;
$
$
$ cat -n example_2.pl
     1  #!/usr/bin/perl -w
     2  use strict;
     3  my $option = $ARGV[0];
     4  my %x = (
     5            0,
     6              [
     7                [ qw (%s %s %s %s %s) ],                      # format array ref
     8                [ qw (Value1 Value2 Value3 Value4 Value5) ],  # header array ref
     9                [ qw (one two three four five) ]              # data array ref
    10              ],
    11            1,
    12              [
    13                [ qw (%s %s %s) ],
    14                [ qw (Value1 Value2 Value3) ],
    15                [ qw (one two three) ]
    16              ],
    17            2,
    18              [
    19                [ qw (%s %s %s) ],
    20                [ qw (Value1 Value4 Value5) ],
    21                [ qw (one four five) ]
    22              ]
    23          );
    24  $option = 0 if (! defined $option) or (! defined $x{$option});
    25
    26  my $fmt  = join(",", @{$x{$option}->[0]})."\n";
    27  my @hdr  = @{$x{$option}->[1]};
    28  my @data = @{$x{$option}->[2]};
    29
    30  printf ($fmt, @hdr);
    31  printf ($fmt, @data);
$
$ perl example_2.pl 1
Value1,Value2,Value3
one,two,three
$
$ perl example_2.pl 2
Value1,Value4,Value5
one,four,five
$
$ perl example_2.pl 3
Value1,Value2,Value3,Value4,Value5
one,two,three,four,five
$
$ perl example_2.pl 4
Value1,Value2,Value3,Value4,Value5
one,two,three,four,five
$
$ perl example_2.pl 0
Value1,Value2,Value3,Value4,Value5
one,two,three,four,five
$
$ perl example_2.pl
Value1,Value2,Value3,Value4,Value5
one,two,three,four,five
$
$

I have been there so many times.

Put each header option in a subroutine.

Build the line with one line one element printf statements, not a long template and a long argument list. It will be more readable, more maintainable, easy to comment and likely runs faster. After all, output is a stream, so throw the pieces into the stream one at a time. Here is a fragment of JAVA I wrote recently:

System.out.print( " " );
System.out.print( creditorName );
System.out.print( " " );
System.out.format( "%8.2f", new Double( d[0] )); // creditorBalance
System.out.print( " " );
System.out.print( s[5] ); // mailDate
System.out.println( " S CMT" );
recCt++ ;

Thanks for taking time to reply and for your suggestion.

Could you try to replace the data by variables (my $Value1 = "one", etc) and then try again ?

I get this:

Value1,Value2,Value3,Value4,Value5
$Value1,$Value2,$Value3,$Value4,$Value5

The only way to make it work was using arrays for the values iso strings.
See the last part of the first post (I am having difficulties in replying to my own post in a separate post).

---------- Post updated at 03:23 PM ---------- Previous update was at 03:19 PM ----------

Would have to convert this idea to Perl and then have it shaped to my problem.
I find a solution already (last part of the first post) and that works for me.

Thanks for the reply though.

That's because you are using the qw operator with your variables. It's not the same.

perlop - perldoc.perl.org

I'm just saying that for good code structure, especially in printf situaitons where a template bit matches a data argument, doing one at a time give you a virtual array of lines where the template is right by the data and any comment, one field a line, give or take the occasional adjacent series of constants.

At a higher level, put all or minority record types in separate subroutines, so the flow is simple to follow: writeHeader, while ... writeData, writeTrailer.