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

I've written some clumsy code to copy a file to stdout (to a web page), and line-wrap it. Some lines in the file are purposefully very long. But we don't want to word-wrap (ala Text::wrap). No, I definitely want to line-wrap at column 80. There must be a more elegant way to do this that this:
sub display_email { my ($fn) = @_; print "<center>file <i>$fn</i></center>\n"; if ( open( EMAIL, $fn ) ) { print "<hr width=600><br>\n<pre>"; while( $_ = <EMAIL> ) { if ( /^Message-Id:/ || /^Content-Type:/ || /^Content-Disposition:/ || /^Content-Transfer/ || /^X-[-\w]*:/ || /^Status: RO/ || /^Mime-Version:/ ) { next; } if ( /^\s/ && $rcvdflag ) { next; } if ( /^Received:/ ) { $rcvdflag = 1; next; } if ( /^[^ ]/ ) { $rcvdflag = 0; } if ( length($_) > 80 ) { chop; do { print substr( $_, 0, 80 ) . "\n"; if (length($_) > 80) { $_ = substr( $_, 80, length($_) - 80 ); } else { $_ = ""; } } while length($_); } else { print $_; } } print "</pre>\n"; close( EMAIL ); } }
Also, in this sub, the code to not copy all the email header noise lines is also somewhat inelegant. Any sugestions? Criticisms?
Regards,
-allan

Replies are listed 'Best First'.
Re: how do I line-wrap while copying to stdout?
by larsen (Parson) on Apr 20, 2001 at 11:34 UTC
    Could this code be good?
    chomp; while (length($_) > 80) { print substr( $_, 0, 80) . "\n"; $_ = substr( $_, 80, length($_) - 80); } print;
    or am I missing something?
      We can condense that a little bit:
      print substr( $_, 0, 80, '')."\n" while length >80; print;
      I left out the chomp to keep the newline....

      Jeroen
      "We are not alone"(FZ)

      Update: Removed two redundant characters.
      2: substr isn't that bad.... see thread below

        You might also benchmark a version that's not destructive to the string, which could possibly win for long strings:
        print "$1\n" while /\G(.{1,80})/gs;
        The advantage here is that the long string in $_ can remain idle and yet scanned, while the substr version requires constant shifting and shortening. And just to factor out the multiple prints, also try:
        print map "$_\n", /\G(.{1,80})/gs;

        -- Randal L. Schwartz, Perl hacker

      thanks. much clearer. I was originally looping on (length($_) gt 0) but then the substr expression gave be run-time warnings. looping on (length gt 80) simplifies it.
      -allan
Re: how do I line-wrap while copying to stdout?
by Zig (Initiate) on Apr 20, 2001 at 12:04 UTC
    First off, should you not be using "chomp" instead of "chop"? It is a lot safer if you just want to get rid of the LF/CR

    Then, you could take the following code (yours):

    if ( length($_) > 80 ) { chop; do { print substr( $_, 0, 80 ) . "\n"; if (length($_) > 80) { $_ = substr( $_, 80, length($_) - 80 ); } else { $_ = ""; } } while length($_); } else { print $_; }

    and replace it with (mine):

    while (length($_) > 80) { print substr($_, 0, 80) . "\n"; $_ = substr($_, 80); } print $_ ;

    Hope this works for you. I am not sure what you want to do with the email headers, perhaps explain a little more on that topic.

    Ciao for now
    Zig

Re: how do I line-wrap while copying to stdout?
by petral (Curate) on Apr 20, 2001 at 21:52 UTC
    Since no one's checked in on this part...
    You can replace the 10 lines dealing with $rcvdflag with:
    $rcvd &&= /^\s/ and next; $rcvd = /^Received:/ and next;
    Basically, set it on "Received:" and keep it set til it doesn't match /^\s/.

    p
Re: how do I line-wrap while copying to stdout?
by ams (Initiate) on Apr 21, 2001 at 00:17 UTC
    Thank you all for your comments. Using many suggestions from various responces, this is what the code looks like now, and it works great!

    sub display_email { my ($fn) = @_; print "<center>file <i>$fn</i></center>\n"; if ( open EMAIL, $fn ) { print "<hr width=600><br>\n<pre>"; $/ = ''; $header = <EMAIL>; # slurp entire mail header in $header =~ s/\n\s+/ /g; # merge continuation lines foreach $_ ( split "\n", $header ) { print "$_\n" unless ( /^Message-Id:/ || /^Content[-\w]*:/ || /^X-[-\w]*:/ || /^Status: RO/ || /^Received:/ || /^Mime-Version:/ ); } # end of headers, now read and display the body of the email $/ = "\n"; print "\n"; while( <EMAIL> ) { chomp; # print lines of the file, line-wrapping at column 80 while ( length($_) > 80 ) { print substr( $_, 0, 80 ), "\n"; $_ = substr( $_, 80 ); } print "$_\n"; } print "</pre>\n"; close EMAIL; } }
    Thanks,
    -allan
Re: how do I line-wrap while copying to stdout?
by BrotherAde (Pilgrim) on Apr 20, 2001 at 16:30 UTC
    as for your code
    while( $_ = <EMAIL> ) { if ( /^Message-Id:/ || /^Content-Type:/ || /^Content-Disposition:/ || /^Content-Transfer/ || /^X-[-\w]*:/ || /^Status: RO/ || /^Mime-Version:/ ) { next; } . . . }
    I'd put the things you want to match the message against into an array and go through that:
    @array=qw (Message-ID: Content-Type: Content-Disposition: and_so_on... ); LINE: while( $_ = <EMAIL> ) { foreach $header (@array) { next LINE if /^$header/; } . . . }
    The code is untested, so I'm not sure it works... but I hope it helps nevertheless.

    BrotherAde