package Pitonyak::StringUtil; #************************************************************ =head1 NAME Pitonyak::StringUtil - File and directory scanning based on regular expressions. =head1 SYNOPSIS use Pitonyak::StringUtil =head1 DESCRIPTION =cut #************************************************************ require Exporter; $VERSION = '1.01'; @ISA = qw(Exporter); @EXPORT = qw( ); @EXPORT_OK = qw( array_width center_fmt compact_space hash_key_width hash_val_width left_fmt num_int_digits num_with_leading_zeros trans_blank trim_fmt trim_space right_fmt smart_printer smart_printer_default ); use Carp; use strict; #************************************************************ =pod =head2 array_width =over 4 =item array_width([arg1], [arg2], ... [argn]) =back Find the maximum width of a list of elements. Each element should either be a scalar or a reference to an array. =cut #************************************************************ sub array_width { my $width = 0; my $this_width; foreach (@_) { $this_width = ( ref($_) ne 'ARRAY' ) ? length($_) : array_width(@$_); $width = $this_width if $this_width > $width; } return $width; } #************************************************************ =pod =head2 center_fmt =over 4 =item center_fmt($width_to_use, @strings_to_format) =back Center the strings in the specified width. the strings are left and right padded to use the entire width. The strings are truncated to fit into the space. =cut #************************************************************ sub center_fmt { # No parameter, return undef if ( $#_ < 1 ) { carp("Usage: center_fmt( )"); return undef; } my $len = $_[0]; my @strings = trim_fmt(@_); my @rc; foreach my $str (@strings) { my $slop = $len - length($str); my $left_space = int( $slop / 2 ); my $right_space = $slop - $left_space; $str = " " x $left_space . $str . " " x $right_space if $slop > 0; push ( @rc, $str ); } return wantarray ? @strings : $strings[0]; } #************************************************************ =pod =head2 compact_space =over 4 =item compact_space(@list_of_strings) =back Removes the spaces from the strings. Each string is potentially modified. Leading and trailing white space is removed. Runs of white space is turned to one space. This modifies the calling parameters. =cut #************************************************************ sub compact_space { # No parameter, return undef if ( $#_ < 0 ) { carp("Usage: compact_space("); return undef; } for (@_) { # # This new method is about four times faster # than the split and join # $_ = join ' ', split /\s+/, $_; # split then join # tr/ //s; # # Save a call to trim_space() at the end! # s/^\s*//; # Remove spaces from front s/\s*$//; # Remove spaces from end } return wantarray ? @_ : $_[0]; } #************************************************************ =pod =head2 hash_key_width =over 4 =item hash_key_width($hash_reference) =back Determine the maximum width of the keys in this hash =cut #************************************************************ sub hash_key_width(\%) { # No parameter, return 0 if ( $#_ < 0 || !UNIVERSAL::isa( $_[0], 'HASH' ) ) { carp("Usage: hash_key_width()"); return 0; } my $hash_ref = shift; my $ref_type = ref($hash_ref); my $width = 0; foreach my $key ( keys %$hash_ref ) { $width = length($key) if length($key) > $width; } return $width; } #************************************************************ =pod =head2 hash_val_width =over 4 =item hash_val_width($hash_reference) =back Determine the maximum width of the values in this hash =cut #************************************************************ sub hash_val_width(\%) { # No parameter, return 0 if ( $#_ < 0 || !UNIVERSAL::isa( $_[0], 'HASH' ) ) { carp("Usage: hash_val_width()"); return 0; } my $hash_ref = shift; my $ref_type = ref($hash_ref); my $width = 0; foreach my $key ( keys %$hash_ref ) { $width = length( $hash_ref->{$key} ) if length( $hash_ref->{$key} ) > $width; } return $width; } #************************************************************ =pod =head2 left_fmt =over 4 =item left_fmt($width_to_use, @strings_to_format) =back Each string has enough spaces appended end so that the total length is C<$width_to_use>. The strings are not truncated to fit into the space. =cut #************************************************************ sub left_fmt { # No parameter, return undef if ( $#_ < 1 ) { carp("Usage: left_fmt( )"); return undef; } my $len = shift; my @rc; foreach my $str (@_) { my $slop = $len - length($str); $str = $str . " " x $slop if $slop > 0; push ( @rc, $str ); } return wantarray ? @rc : $rc[0]; } #************************************************************ =pod =head2 num_int_digits =over 4 =item num_int_digits($number) =back This returns the length of a number =cut #************************************************************ sub num_int_digits { # No parameter, return undef if ( $#_ < 0 ) { carp("Usage: num_int_digits("); return undef; } return length( sprintf( "%d", $_[0] ) ); } #************************************************************ =pod =head2 num_with_leading_zeros =over 4 =item num_with_leading_zeros(($width_to_use, @numbers_to_format) =back Returns N-digit strings representing the number with leading zeros. Modulo is used to chop the number. If C 0>, then leading negative signs are included. =cut #************************************************************ sub num_with_leading_zeros($$) { # No parameter, return undef if ( $#_ < 1 ) { carp("Usage: num_with_leading_zeros( "); return undef; } my $num_digits = shift; my @rc; foreach (@_) { my $num = $_; my $rvalue = ""; if ( $num_digits != 0 ) { if ( $num_digits < 0 ) { $num_digits = -$num_digits; if ( $num < 0 ) { --$num_digits; $rvalue = "-"; } } $num = -$num if $num < 0; my $tmp = sprintf "%d", $num; my $lead_zero = $num_digits - length($tmp); if ( $lead_zero > 0 ) { $rvalue .= "0" x $lead_zero . $tmp; } else { $rvalue .= substr $tmp, $[ - $lead_zero; } } push ( @rc, $rvalue ); } return wantarray ? @rc : $rc[0]; } #************************************************************ =pod =head2 trans_blank =over 4 =item trans_blank($value, [$default]) =back Returns $value if it is defined with length greater than zero and C<$default> if it is not. If $default is not included, then an empty string is used for C<$default>. =cut #************************************************************ sub trans_blank { # No parameter, return undef if ( $#_ < 0 ) { carp("Usage: trans_blank( [])"); return undef; } my $default_value = ""; $default_value = $_[1] if $#_ > 0; $default_value = $_[0] if defined( $_[0] ) && length( $_[0] ) > 0; return $default_value; } #************************************************************ =pod =head2 trim_fmt =over 4 =item trim_fmt($width_to_use, @strings_to_format) =back Trim all strings so that their length is not greater than C<$width_to_use>. =cut #************************************************************ sub trim_fmt { # No parameter, return undef if ( $#_ < 1 ) { carp("Usage: trim_fmt( )"); return undef; } my $len = shift; my @rc; foreach my $str (@_) { my $slop = $len - length($str); $str = substr( $str, $[, $len ) if $slop < 0; push ( @rc, $str ); } return wantarray ? @rc : $rc[0]; } #************************************************************ =pod =head2 trim_space =over 4 =item trim_space(@strings_to_format) =back Remove leading and trailing white space. The parameters are modified. =cut #************************************************************ sub trim_space { # No parameter, return undef if ( $#_ < 0 ) { carp("Usage: trim_space("); return undef; } for (@_) { s/^\s*//; # Remove spaces from front s/\s*$//; # Remove spaces from end # # The following takes longer: # #($_) = ($_ =~ /^\s*(.*?)\s*$/); } return wantarray ? @_ : $_[0]; } #************************************************************ =pod =head2 right_fmt =over 4 =item right_fmt($width_to_use, @strings_to_format) =back Each string has enough spaces prepended end so that the total length is C<$width_to_use>. The strings are not truncated to fit into the space. =cut #************************************************************ sub right_fmt { # No parameter, return undef if ( $#_ < 1 ) { carp("Usage: right_fmt( )"); return undef; } my $len = shift; my @rc; foreach my $str (@_) { my $slop = $len - length($str); $str = " " x $slop . $str if $slop > 0; push ( @rc, $str ); } return wantarray ? @rc : $rc[0]; } #************************************************************ #** ** #** Input: left indent to print ** #** how to grow left indent for recursive printing ** #** Separator for items (generally "\n") ** #** list of things to print ** #** ** #** Output: String you desire to print ** #** ** #** Notes: ** #** Apart from being stuck with the output format, ** #** this has problems with references to references ** #** printing ony the text REF rather than simply ** #** recursing the references which would not be ** #** that difficult. ** #** ** #************************************************************ #************************************************************ =pod =head2 smart_printer =over 4 =item smart_printer($left, $left_grow, $separator, @Things_to_print) =back Attempts to print almost any object in a pretty fashion. The C<$left> parameter determines what is printed before each thing printed. The C<$left_grow> parameter determines the new C<$left> if smart_printer() is recursively called. the C<$separator> is printed between each item. A Scalar is printed. A Hash is printed as C<{ key =E value key =E value }> An Array is printed as C<( value value )> Keys and values can also be references. =cut #************************************************************ sub smart_printer { if ( $#_ < 3 ) { carp( "usage: smart_printer( )" ); return undef; } my $indent = shift; my $indent_grow = shift; my $item_separator = shift; my $txt = ''; foreach my $thing_to_print (@_) { if ( !defined($thing_to_print) ) { $txt .= $indent . 'undef' . $item_separator; } else { my $ref_type = ref $thing_to_print; if ( !$ref_type ) { $txt .= "$indent$thing_to_print$item_separator"; } elsif ( $ref_type eq 'SCALAR' ) { $txt .= smart_printer( $indent, $indent_grow, $item_separator, $$thing_to_print ); } elsif ( $ref_type eq 'ARRAY' ) { $txt .= "$indent($item_separator"; foreach my $array_thing (@$thing_to_print) { $txt .= smart_printer( $indent . $indent_grow, $indent_grow, $item_separator, $array_thing ); } $txt .= "$indent)$item_separator"; } elsif ( UNIVERSAL::isa( $thing_to_print, 'HASH' ) ) { my $width = hash_key_width(%$thing_to_print); # # Remember that each hash has one universal iterator # recursive nesting will therefore cause stranger # results than a simple infinite loop. # $txt .= "$indent\{$item_separator"; my ( $key, $value ); while ( ( $key, $value ) = each %$thing_to_print ) { $txt .= $indent . $indent_grow . left_fmt( $width, $key ) . ' => '; $value = '' if !defined($value); if ( !ref($value) ) { $txt .= "$value$item_separator"; } elsif ( ref($value) eq 'SCALAR' ) { $txt .= smart_printer( '', $indent_grow, $item_separator, $value ); } else { $txt .= $item_separator; $txt .= smart_printer( $indent . $indent_grow . $indent_grow, $indent_grow, $item_separator, $value ); } } $txt .= "$indent}$item_separator"; } else { $txt .= "$indent$ref_type$item_separator"; $txt .= "$indent<$item_separator"; $txt .= smart_printer( $indent . $indent_grow, $indent_grow, $item_separator, $$thing_to_print ); $txt .= "$indent>$item_separator"; } } } return $txt; } #************************************************************ =pod =head2 smart_printer_default =over 4 =item smart_printer_default(Things to print) =back Each parameter is printed using smart_printer() using default parameters. the items are printed with no initial left indent, recursive indents using two extra spaces, and a new line for the item separator. =cut #************************************************************ sub smart_printer_default { return smart_printer( '', ' ', "\n", @_ ); } #************************************************************ =pod =head1 COPYRIGHT Copyright 1998-2002, Andrew Pitonyak (perlboy@pitonyak.org) This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 Modification History =head2 March 13, 1998 Version 1.00 First release =head2 September 10, 2002 Version 1.01 Changed internal documentation to POD =cut #************************************************************ 1;