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

Hi there inmates, is it too early in the year for one of my dumb questions? No? Okay then! I want my error messages to be output in a nice little box made of asterisks (whether to STD or a file) - no real problem there. However these messages may be multiple lines of different sizes and the text should be centered(based on the largest line supplied)! I had a look at the manual and thought printf/sprintf would suffice - not as far as I can see. How about pack? This may do but I can't seem to find any way of ensuring the text is centre justified. Am I on the right tracks or is there an abvious way to do this? Any ideas? Thanks in aniicipation, Ronnie

20050203 Edit by castaway: Changed title from 'Pack Problem?'

Replies are listed 'Best First'.
Re: Special formatting of error messages
by davido (Cardinal) on Feb 01, 2005 at 17:51 UTC

    Sounds like something that formats would be useful for. Hmm... that's the first time I've ever said that. I've probably been missing out on a lot of fun.

    Without formats, you just have to do a two-pass approach. First, determine the length of the widest string being output. length is handy for that. Second, each string needs to be padded with ( $length_max - $length_current ) / 2 leading space characters, and with $length_max - $new_current_length space characters trailing.


    Dave

      Yes that was the original idea that I had but ..... (Now this may be the daftest part of the question) how is the space padding applied? Cheers, Ronnie PS I may be missing something very obvious here but for the life of me I can't see it!

        I kind of described it already, but here's one brainstorm that is untested:

        use strict; use warnings; my $string = <<HERE; A long time ago in a galaxy far far away a great adventure took place. HERE sub textbox { my( @lines ) = split /\n/, $_[0]; chomp @lines; my $longest = 0; foreach my $line ( @lines ) { my $size = length $line; $longest = $size if $size > $longest; } foreach my $line ( @lines ) { my $prepad = ' ' x ( ( $longest - length( $line ) ) / 2 ); $line = $prepad . $line; $line .= ' ' x ( $longest - length( $line ) ); $line = '* ' . $line . ' *'; } my $outline = '*' x ( $longest + 4 ); push @lines, $outline; unshift @lines, $outline; return join( "\n", @lines ) . "\n"; } print textbox( $string );

        This could use some streamlining, but I left it reasonably verbose so that it would be clear what's going on. Enjoy!


        Dave

Re: Special formatting of error messages
by eieio (Pilgrim) on Feb 01, 2005 at 17:50 UTC
    Text::Autoformat may help. You can specify margins, justification, etc. It's very flexible.
Re: Special formatting of error messages
by trammell (Priest) on Feb 01, 2005 at 17:48 UTC
    I'd think pack() is not suited for this. I would use a module like Text::Wrap and print()/printf().
Re: Special formatting of error messages
by osunderdog (Deacon) on Feb 01, 2005 at 18:32 UTC

    This quick and dirty but a fun brain teaser.

    use strict; my @lines = <DATA>; my $maxsize = 0; map {chomp; $maxsize = ($maxsize>length)?($maxsize):(length)} @lines; print "max size: $maxsize\n"; my $bar = '*' x ($maxsize +4) . "\n"; my $format = '%' . $maxsize . '.' . $maxsize . 's'; foreach (@lines) { print($bar); printf("* $format *\n", $_); print($bar); } __DATA__ some lines that are of different sizes Some small really small. ----------------some large--------------------- --------------------------------------------------some huge----------- +--------------------------------------- ...

    "Look, Shiny Things!" is not a better business strategy than compatibility and reuse.

      This works perfectly thanks. But....it leads me to a small additional question. Map works on a list and sets up the value of maxsize. This looks like a regex (horror of horrors!) I'm not too familiar with anything but simple regexs! Sorry to be a pest but can you confirm my understanding of this. Assuming map works on a list, the code on the right hand of the ? uses the length function on the row being processed and if it's greater than $maxsize (which was initialised with a value of zero) then maxsize is updated with this new value ( I think). The bit that's confusing me is after the ? What is the purpose of the ($maxsize):(length)? It can't be returning the value of length into $maxsize as that would render the first > test as pointless, wouldn't it?
      Cheers, Thick Ronnie

        Let me break this down:

        my $maxsize = 0; map {chomp; $maxsize = ($maxsize>length)?($maxsize):(length)} @lines;

        If I were to write this using the 'Software Engineer' side of my brain, it would look like:

        my $maxsize = 0; foreach my $line (@lines) { chomp $line; #remove \n from the line my $lineLength = length($line); #Determine length of line #if line is longer than the longest #line I've seen so far then make it $maxsize. if($lineLength>$maxsize) { $maxsize = $lineLength; } }
        code hasn't been tested

        It sounds like the part that is throwing you is the '?:' conditional operator:

        $result = ($x > $y)?(14):(-5)

        It's a one line conditional. It's documented in perldoc perlop (look for 'Conditional Operator'). The condition is evaluated and if true, the first value is assigned into $result (14) if it's false, the second value is assigned into $result (-5).

        I hadn't done it this way before and I thought your question was a good opportunity to try it out. I've seen it before in code, just haven't had a chance to see if it suits me.

        To any monks that have read this far. What's the minimal code for finding the lonest line in an array?


        "Look, Shiny Things!" is not a better business strategy than compatibility and reuse.