How can I align columns with Perl printf when this value can be a number or a string?

I am trying to output a document that looks like this (more details at http://pastebin.com/dpBAY8Sb ):

10.1.1.1 100 <unknown> <unknown> <unknown> <unknown> <unknown> 72.12.148.186 94 Canada Hamilton ON 43.250000 -79.833300 0.00 72.68.209.149 24 United States Richmond Hill NY 40.700500 -73.834500 611.32 72.192.33.34 4 United States Rocky Hill CT 41.657800 -72.662700 657.48 

I can not find how to format the output, I have to have floating poing and format the distance between the columns. My current code looks something like this.

 if (defined $longitude){ printf FILE ("%-8s %.6f","",$longitude); }else{ $longitude = "<unknown>"; printf FILE ("%-20s ",$longitude); } 

Additional "" throws the entire column, and it looks like this (for more details see http://pastebin.com/kcwHyNwb ).

 10.1.1.1 100 <unknown> <unknown> <unknown> <unknown> <unknown> 72.12.148.186 94 Canada Hamilton ON 43.250000 -79.833300 0.00 72.68.209.149 24 United States Richmond Hill NY 40.700500 -73.834500 571.06 
+4
source share
3 answers

The trick in these problems is to minimize code branching and duplication as much as possible. printf inline is not special. It takes a format argument and a list, so you just need to give it the right things and then call it once. Alternatively, you can be more accurate with your validation using something like Scalar :: Util looks_like_number , so that your data is closer to what you would expect:

 use Scalar::Util qw(looks_like_number); while( <DATA> ) { chomp(my $longitude = $_ ); my( $format, @arguments ) = do { looks_like_number $longitude ? ( "|%-8s %.6f|\n", '', $longitude ) : ( "| %16s|\n", '<unknown>' ) }; printf $format, @arguments; } __END__ 1 2.12345678 undef 0 fred 

Now you have one printf , and you do not need the extra variable $longitude2 . There are a few extra tricks you can play with printf so you can sort the columns automatically, but we won’t worry about it right now. :)

+2
source

I think your format string for the unknown is a bit off.

Here is my example:

 foreach $longitude (1,2.12345678,undef) { if (defined $longitude){ printf ("|%-8s %.6f|\n","",$longitude); }else{ $longitude2 = "<unknown>"; printf ("| %16s|\n",$longitude2); } } 

And the output (with " %16s" for an unknown format. I added vertical columns to clearly see the alignment):

 | 1.000000| | 2.123457| | <unknown>| 

PS If you look at your pattern matching and what you created, then the <unknown> state also seems to be misaligned. To check, use the same trick I did above, including the format string for each field in | to see where one field ended and another begins.

+1
source

Personally, I would pass them through the same printf

  # You can choose prefered way of laying out the logic ! $text = sprintf ("%.6f",$longitude); # or $text = '<unknown>' ; 

and then

  printf ("| %16s|\n",$text); 

Then, if 16 is 20, both bits will adapt in the step.

0
source

Source: https://habr.com/ru/post/1307001/


All Articles