http://www.perlmonks.org?node_id=1009833

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

I am trying to figure out how to split a string based on length, while also keeping words together (word boundary). I have the split based on length part worked out, but can't figure out how to not split a word.

More info...
I am building a menu system. I have a subroutine for adding menu items that splits strings based on length, to maintain the width of the menu, but if I pass a string that contains a bunch of words, that exceed the menu's width, words get cut and makes the menu look very bad.

Example:

+------------------+ | This is my menu | +------------------+ | | +------------------+

The menuAdd() subroutine adds the pipes at the left and right side of the menu and either pads a short string with spaces or splits a long string into multiple lines.

My dilema:

menuAdd('A really long string that I want to add to the awesome menu s +ystem',20);
produces
+------------------+ | This is my menu | +------------------+ | | | A really long st | | ring that I want | | to add to the aw | | esome menu syste | | m | +------------------+
I want it to produce
+------------------+ | This is my menu | +------------------+ | | | A really long | | string that I | | want to add to | | the awesome menu | | system | +------------------+

This is what I have for the menuAdd() subroutine

sub menuAdd{ my $text = shift; my $menuWidth = shift; my $textLength = length($text); $menuWidth-=4; #shorten $menuWidth to leave room for | and a space + on left and right sides of the menu if ( $textLength <= $menuWidth ){ #add spaces to end of $text to maintain menuWidth print "| " . padSpaces($text,$menuWidth) . " |\n"; } elsif( $textLength > $menuWidth ){ #split $text into separate lines to maintain menuWidth my @textArray = split(/(.{$menuWidth})/, $text); print @textArray; foreach my $line (@textArray){ print "| " . padSpaces($line,$menuWidth) . " |\n"; #print "| $line |\n"; } } else { print "******SHOULD NEVER GET HERE******\n"; } }

Replies are listed 'Best First'.
Re: How to split a string based on length or word boundary
by BrowserUk (Patriarch) on Dec 21, 2012 at 02:40 UTC

    As long as your text doesn't contain any single words longer than your width:

    $s = 'A really long string that I want to add to the awesome menu sys +tem';; print "| $_ |" for map substr( $_ . ' 'x16, 0, 16 ), $s =~ m[(.{1,16}) +\s+]g;; | A really long | | string that I | | want to add to | | the awesome menu |

    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

    RIP Neil Armstrong

      Brilliant++!

      Just needs a couple of tweaks:

      print "| $_ |\n" for map substr($_ . ' ' x 16, 0, 16), $s =~ m[(.{1,16 +})(?:\s+|$)]g; # ^^ + ^^^^^^^^^

      (Otherwise, the final word won’t be printed unless it happens to be followed by whitespace.)

      Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

        Good point about the $++

        The \n is provided by -l in my code.


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.

        RIP Neil Armstrong

      That works! Thanks for the quick reply. I changed the elsif code to
      print "| $_ |\n" for map substr( $_ . ' 'x$menuWidth, 0, $menuWidth ), + $text =~ m[(.{1,$menuWidth})\s+]g;

        As Athanasius pointed out above, you need to change that to:

        print "| $_ |\n" for map substr( $_ . ' 'x$menuWidth, 0, $menuWidth ), + $text =~ m[(.{1,$menuWidth})(?:\s+|$)]g; #..................................................................... +.............................^^^^^^^^^

        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.

        RIP Neil Armstrong

Re: How to split a string based on length or word boundary
by Anonymous Monk on Dec 21, 2012 at 03:28 UTC

    See Perl6::Form and Text::Wrap and Text::Autoformat

    $ perl -lne " use Perl6::Form; print form q{{IIIIIIIIIIIIIIII}}, $_ " A really long string that I want to add to the awesome menu system A really long string that I want to add to the awesome menu system

    $ perl -lne " use Text::Wrap qw/ wrap $columns /; $columns = 16; print + wrap(q//,q//,$_); " A really long string that I want to add to the awesome menu system A really long string that I want to add to the awesome menu system ^Z

    $ perl -lne " use Text::Autoformat; print autoformat $_, {qw{ left 1 r +ight 17 }} " A really long string that I want to add to the awesome menu system A really long string that I want to add to the awesome menu system ^Z

    update: although, you might want to see Text::Table, IO::Prompter, Term::Interact and Term::Interact example