Beefy Boxes and Bandwidth Generously Provided by pair Networks
There's more than one way to do things

Nicely format a list or a string

by ZZamboni (Curate)
on May 11, 2000 at 19:29 UTC ( #11166=snippet: print w/replies, xml ) Need Help??
Description: The _sprintlist subroutine takes a list reference and returns a string where the list is nicely formatted. You can specify the tag for the first line, the indentation, the separator used, the line width, and even a prefix for every line. Play with the different options to see how they affect the output. For example, the following:
@list=('aaa'..'aax'); print _sprintlist(\@list,"Members:",undef,10,undef,60,"* ");
will produce:
* Members: aaa, aab, aac, aad, aae, aaf, aag, aah, aai, * aaj, aak, aal, aam, aan, aao, aap, aaq, aar, * aas, aat, aau, aav, aaw, aax
Any arguments given as "undef" (or not given) take on their default values.

The _sprintstr function takes a string instead of a list, and uses _sprintlist to print the string nicely formatted. Most of the arguments are the same.

I have also included a sample Help() function to print command summaries with their help strings. This assumes the hash %COMMANDS is indexed by command name, and each element is a hash ref with elements "Sum" and "Desc".

A named-argument interface is left as an exercise to the reader :-)


Updated: Added sample input and output for Help().

# Returns an indented string containing a list. Syntax is:
# _sprintlist($listref[, $firstline[, $separator[, $indent
#             [, $break[, $linelen[, $lineprefix]]]]])
# All lines are indented by $indent spaces (default 0).
# If $firstline is given, that string is inserted in the
# indentation of the first line (it is truncated to $indent spaces
# unless $break is true, in which case the list is pushed to the next
# line). Normally the list elements are separated by commas and spaces
# ", ". If $separator is given, that string is used as a separator.
# Lines are wrapped to $linelen characters, unless it is specified as
# some other value. A value of 0 for $linelen makes it not do line wra
# If $lineprefix is given, it is printed at the beginning of each line
sub _sprintlist {
  my $listref=shift or return;
  my @list=@$listref;
  my $fline=shift;
  my $separator=shift || ", ";
  my $indent=shift;
  $indent=0 unless defined($indent);
  my $space=" " x $indent;
  my $break=shift || 0;
  my $linelen=shift;
  $linelen=80 unless defined($linelen);
  my $lp=shift||"";
  if (!$break || length($fline)<=$indent) {
    $fline=substr("$fline$space", 0, length($space));
  else {
    # length($fline)>$indent
  my $str="";
  my $line="";
  foreach (@list) {
    $line.=$separator if $line;
    if ($linelen && length($line)+length($_)+length($space)>=$linelen)
+ {
  $str=~s/^/$lp/mg if $lp;
  return $str;

# Gets a string, and returns it nicely formatted and word-wrapped. It
# uses _sprintlist as a backend. Its syntax is the same as
# _sprintlist, except that is gets a string instead of a list ref, and
# that $separator is not used because we automatically break on white
# space.
# Syntax: _sprintstr($string[, $firstline[, $indent[, $break[, $linele
#               [, $lineprefix]]]]]);
# See _sprintlist for the meaning of each parameter.
sub _sprintstr {
  my ($str, $fl, $ind, $br, $len, $lp)=@_;
  # Split string into \n-separated parts.
  my @strs=($str=~/([^\n]+\n?|[^\n]*\n)/g);
  # Now process each line separately
  my $s;
  my $result;
  foreach $s (@strs) {
    # Store end of lines at the end of the string
    my $eols=($s=~/(\n*)$/)[0];
    # Split in words.
    my @words=(split(/\s+/, $s));
    $result.=_sprintlist(\@words,$fl," ",$ind,$br,$len, $lp).$eols;
    # The $firstline is only needed at the beginning of the first stri
  return $result;

And the sample Help() function:

# Sample %COMMANDS in the format assumed.
  add => { Sum => 'add NAME to LIST',
           Desc => 'Add the name to the mailing list.' },
  all => { Sum => 'all [-a|-l]',
           Desc => 'Print out all the aliases and mailing lists. '.
                   'With -a only prints aliases, with -l only prints '
                   'lists.' },
  delete => { Sum => 'delete LIST,...',
              Desc => 'Delete mailing lists. Also deleted owner-LIST a
+nd '.
                      'LIST-owner if they exist.' },
  list => { Sum => 'list [-1|-x|-s|-n] LIST|/regex/,...',
            Desc => 'List information about the given aliases. If list
+ '.
                    'name starts with "/" it is assumed to be a regula
+r '.
                    'expression. '.
                    '-1 lists members one per line, '.
                    '-x lists members all on a single line. '.
                    '-s lists in Unix alias format. '.
                    '-n only shows list names.' },

# Usage message
sub Help {
  print "The current commands are:\n";
  my $cmd;
  foreach $cmd (sort keys %COMMANDS) {
      print _sprintstr($COMMANDS{$cmd}->{Desc},
  print "Use 'quit' or '^D' to quit.\n";
With the given definition of %COMMANDS, Help() produces:
The current commands are:
add NAME to LIST              Add the name to the mailing list.
all [-a|-l]                   Print out all the aliases and mailing li
+sts. With
                              -a only prints aliases, with -l only pri
delete LIST,...               Delete mailing lists. Also deleted owner
+-LIST and
                              LIST-owner if they exist.
list [-1|-x|-s|-n] LIST|/regex/,...
                              List information about the given aliases
+. If list
                              name starts with "/" it is assumed to be
+ a
                              regular expression. -1 lists members one
+ per
                              line, -x lists members all on a single l
+ine. -s
                              lists in Unix alias format. -n only show
+s list
Use 'quit' or '^D' to quit.
Replies are listed 'Best First'.
RE: Nicely format a list or a string
by dempa (Friar) on May 12, 2000 at 00:41 UTC
    An obvious use for this snippet is to create nicely formatted email aliases in sendmail. As a Unix System Admin I often get long lists of people that should be converted to an sendmail alias, and this snippet saves a lot of time. Nice one, ZZamboni.
      Interestingly, as you may see from my examples, these functions come from a program I'm writing to manage mail aliases and lists... :-)
Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: snippet [id://11166]
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others browsing the Monastery: (4)
As of 2018-06-23 20:18 GMT
Find Nodes?
    Voting Booth?
    Should cpanminus be part of the standard Perl release?

    Results (125 votes). Check out past polls.