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

Hi guys, earlier I posted a question asking how to go about formatting output to screen into columns. Got an excellent couple of responses to my question, which I've been playing with.

Unfortunately, I am having issues with one of the instances of use of this in my script... and can't for the life of me work out why this is so.

Now - the one below, is working fine :

while (@list) { my @row = splice(@list, 0, 4); format STDOUT = @<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<< +<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<< @row . write; }

But the next one, for some reason, just prints blank space...

while (@UACS) { my @rows = splice(@UACS, 0, 5); print "@rows"; format STDOUT = @<<<<<<<<<<<< @<<<<<<<<<<<< @<<<<<<<<<<<< @<<<<<<<<<<<< @<<<<< +<<<<<<< @rows . write; }

To confirm things *should* be ok from an input perspective, I threw in the print "@rows"; line to see what was being passed to STDOUT - and it appears as it should - see an example of two of the resulting lines below:

TECH SCANNER WEBSERVER TALKTOME TRAINING TESTING MOBILE TECHADMIN CIRCADMIN MANAGERS

So... to me the input seems ok, the command seems the same as the one which is working - aside from a slight change in the formatting itself. Any suggestions on where it is I'm going wrong with this?

Replies are listed 'Best First'.
Re: Assistance fixing a "format" call
by Marshall (Canon) on Apr 02, 2012 at 02:51 UTC
    When I first saw this format thing, I thought oh, how cool. After some experience with it, I have changed my mind! The old standby printf just works out better in practice, at least for me. My advice would be to get rid of that "format" stuff.
    #!/usr/bin/perl -w use strict; while (<DATA>) { my @tokens = split ' ', $_; printf "%-10s %-10s %-10s %-10s %-10s\n", @tokens; } =prints TECH SCANNER WEBSERVER TALKTOME TRAINING TESTING MOBILE TECHADMIN CIRCADMIN MANAGERS =cut __DATA__ TECH SCANNER WEBSERVER TALKTOME TRAINING TESTING MOBILE TECHADMIN CIRCADMIN MANAGERS
    Update: also with standard printf in Perl (or even C), you can dynamically generate the "format spec" - should that be necessary and its almost never necessary.
    while (<DATA>) { my @tokens = split ' ', $_; my $format = '%-10s ' x @tokens; printf "$format\n", @tokens; }
    It is important to realize that with "format spec width" like %-10s specifies the minimum width. If the string is longer than that it will still be printed although the columns for that line won't line up. This almost always what you want - one goofy looking line in a 200 line report. The trailing space after the %-10s ensures that there will at least one blank space between columns and that is almost always what is desired. Extremely rigid, fixed field, "card punch image" formats are very rare nowadays. Adjust the column widths so that they work "almost all of the time" - when the outlier line comes along, humans are very good at integrating and seeing past that single line.

    If you absolutely insist upon using a "format" despite my recommendation to the contrary, put the definition outside of the while() loop.

      Hmmm thanks for that Marshall. I had had a look at printf, but by that point I'd already gone down the format path as it was the first recommendation I'd received - and it seemed to work fine for me first up.

      I should certainly be able to modify some printf calls to get the output I'm after. Just need it to look all purdy on-screen so the user can browse the data relatively easily to pick out the one they're after. Edit: modified my code while I was drafting up this response, and it does indeed do what I'm after, so thanks again for that.

      Having said that... I'd still love to know why it is that what I'm using at present isn't working as expected. I'm sure it must be something fundamental I'm doing wrong - but it would be nice to know for future reference (if not only for myself, but others that might be looking at doing similar) what it is. It's certainly been rather frustrating to try and work it out ;)

        well one possibility is that "write" writes to the currently selected filehandle. Try "write STDOUT;"

        But since you say that you are getting at least some kind of line, the leading spaces in the 2nd format statement may also be an issue - this is a finicky critter - one reason why I don't recommend it. The first answer on Monks is not always the "best answer" - and I would include myself in that general statement also!.

        In the future, I think you will wind up being much happier with printf().

        Anyway, the difference that I see is this: (2nd format does not start in column 1).

        format STDOUT = @<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<<<<<<<< +<<<<<<<<<<<< @<<<<<<<<<<<<<<<<<<<<<<<<< @row . not the same as: format STDOUT = @<<<<<<<<<<<< @<<<<<<<<<<<< @<<<<<<<<<<<< @<<<<<<<<<<<< @<<<<< +<<<<<<< @rows .
        Update: After looking at the doc's, the "." - to end the format - has to be in column 1. It doesn't say that the format itself must start in column 1, but at the moment I am at a loss for any other ideas. The two statements appear to be so similar that that's the only thing I can think of at the moment.

        I did look at what it will do if "the data doesn't fit" and you get #'s in that field. This is almost always not what is desired. I wrote a recent post at printf exact field width of floating point number that talks about using sprintf() to decide how wide a field will actually be printed and how to use a different format spec in printf() if it "doesn't fit" within a fixed number of characters (in this case using %g instead of %f). This is extremely rare. (update: I liked BrowserUK's answer in that thread also, if you give %g "enough room to work", it will do a "good job").

        All of which will be better than the "format" idea for numeric values. For strings, this whole idea of how to display the decimal number is mute. For strings, the issue is whether you are going to truncate the string or allow the columns for that line to "go astray" if the value doesn't "fit".

        In general printing "something" is better than printing "nothing or just #'s". But almost always, print the data to the precision needed and if some weird line doesn't line up, then so be it. Always add an explicit space between format specifications to ensure at least one blank space between values.

        In summary, this "format" idea was a good one, but it doesn't work out well in practice.

Re: Assistance fixing a "format" call
by Anonymous Monk on Apr 02, 2012 at 05:30 UTC

    Any suggestions on where it is I'm going wrong with this?

    Well, you posted code which isn't complete and which doesn't demonstrate your bug :) I see you took a page out of Basic debugging checklist, and that is a good step towards asking a question effectively, but as you can see from my complete snippet, what you've shown so far demonstrates no bug

    #!/usr/bin/perl -- use strict; use warnings; my @stuff; push @stuff, [ qw[ TECH SCANNER WEBSERVER TALKTOME TRAINING ] ]; push @stuff, [ qw[ TESTING MOBILE TECHADMIN CIRCADMIN MANAGERS ] ]; for my $row ( @stuff ){ my @rows = @$row; print "@rows\n"; format STDOUT = @<<<<<<<<<<<< @<<<<<<<<<<<< @<<<<<<<<<<<< @<<<<<<<<<<<< @<<<<< +<<<<<<< @rows . write; } __END__ TECH SCANNER WEBSERVER TALKTOME TRAINING TECH SCANNER WEBSERVER TALKTOME TRAINI +NG TESTING MOBILE TECHADMIN CIRCADMIN MANAGERS TESTING MOBILE TECHADMIN CIRCADMIN MANAGE +RS

      Hi Anon, thanks for your response. Thanks very much also for the pointer to the debugging checklist - that's one I'd not come across before, so I've bookmarked that one to refer back to later... could come in very handy, methinks.

      With regard to your comment about the incomplete code - I just took the snippet out of my script that was being problematic - the section before it simply populates the array with the data (which I confirmed good in previous tests, and provided an example of above). Should I have provided more in order to give better context to the issue or...?

      Please note - not trying to be defensive or snarky here - I'm new to posting to the site, and have been trying to ensure I get/do things right so as to not offend the locals. Appears I may have missed the mark this time around ;)

      I'll have to go back later and look at my older version of the script to see if I can work this out - due to time constraints I've moved on from that in the mean-time and have, as suggested, ditched the format stuff and moved on to printf, which is doing the job admirably.

        When you present code here, the best is to give us something that is as short as possible, that actually runs and that reproduces the problem. I didn't run your two "format" code examples because it was "too much effort" - make it easy for us to click, download and run. Otherwise there is some guesswork involved.

        If you have a single file that you use, use a __DATA__ segment for that data. That file handle is already "open" and you can access it:  while(<DATA>){..}.

        I actually ran the code that I gave you and I know that it at least runs on my machine. Please give us the same in return.

        If some code is not working, give us the exact code (preferably something that we can run easily) and the exact error message - don't give us the short explanation like you'd give your 35th cousin in Chickenblister, Wyoming where-ever the heck that is! Get down and dirty with the details!

        If you look at my first answer to your post, you will see that it is a complete runnable program, including a __DATA__ segment. This "=" in column one (eg =prints) is a "trick" - its actually a use of POD - Plain Old Documentation. I often use that in Monk posts so that I can put some comments (like the actual program output) in the code without interfering with the program or its __DATA__ segment. For code without a __DATA__ segment, you can just put __END__ and all after that are just comments (doesn't matter to Perl).

        Having said that, welcome to Monks!