Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 324 | agaran | 1 | # Text::Table - Organize Data in Tables |
| 2 | package Text::Table; |
||
| 3 | use strict; |
||
| 4 | use warnings; |
||
| 5 | |||
| 6 | use Text::Aligner qw( align); |
||
| 7 | |||
| 8 | BEGIN { |
||
| 9 | use Exporter (); |
||
| 10 | our $VERSION = ('$Revision: 1.114 $' =~ /(\d+.\d+)/)[ 0]; |
||
| 11 | |||
| 12 | } |
||
| 13 | |||
| 14 | use overload |
||
| 15 | '""' => 'stringify', |
||
| 16 | ; |
||
| 17 | |||
| 18 | ### User interface: How to specify columns and column separators |
||
| 19 | |||
| 20 | sub _is_sep { |
||
| 21 | local $_ = shift; |
||
| 22 | defined and ( ref eq 'SCALAR' or ( ref eq 'HASH' and $_->{ is_sep})); |
||
| 23 | } |
||
| 24 | |||
| 25 | sub _parse_sep { |
||
| 26 | local $_ = shift; |
||
| 27 | defined $_ or $_ = ''; |
||
| 28 | my ( $title, $body); |
||
| 29 | if ( ref eq 'HASH' ) { |
||
| 30 | ( $title, $body) = @{ $_}{ qw( title body)}; |
||
| 31 | } else { |
||
| 32 | ( $title, $body) = split /\n/, $$_, -1; |
||
| 33 | } |
||
| 34 | $body = $title unless defined $body; |
||
| 35 | align( 'left', $title, $body); |
||
| 36 | { |
||
| 37 | is_sep => 1, |
||
| 38 | title => $title, |
||
| 39 | body => $body, |
||
| 40 | } |
||
| 41 | } |
||
| 42 | |||
| 43 | sub _parse_spec { |
||
| 44 | local $_ = shift; |
||
| 45 | defined $_ or $_ = ''; |
||
| 46 | my $alispec = qr/^ *(?:left|center|right|num|point|auto)/; |
||
| 47 | my ( $title, $align, $align_title, $align_title_lines, $sample); |
||
| 48 | if ( ref eq 'HASH' ) { |
||
| 49 | ( $title, $align, $align_title, $align_title_lines, $sample) = |
||
| 50 | @{ $_}{ qw( title align align_title align_title_lines sample)}; |
||
| 51 | } else { |
||
| 52 | my $alispec = qr/&(.*)/; |
||
| 53 | if ( $_ =~ $alispec ) { |
||
| 54 | ( $title, $align, $sample) = /(.*)^$alispec\n?(.*)/sm; |
||
| 55 | } else { |
||
| 56 | $title = $_; |
||
| 57 | } |
||
| 58 | defined and chomp for $title, $sample; |
||
| 59 | } |
||
| 60 | defined or $_ = [] for $title, $sample; |
||
| 61 | defined $align and length $align or $align = 'auto'; |
||
| 62 | ref eq 'ARRAY' or $_ = [ split /\n/, $_, -1] for $title, $sample; |
||
| 63 | unless ( |
||
| 64 | ref $align eq 'Regex' or |
||
| 65 | $align =~ /^(?:left|center|right|num\(?|point\(?|auto)/ |
||
| 66 | ) { |
||
| 67 | _warn( "Invalid align specification: '$align', using 'auto'"); |
||
| 68 | $align = 'auto'; |
||
| 69 | } |
||
| 70 | defined $align_title and length $align_title or $align_title = 'left'; |
||
| 71 | unless ( $align_title =~ /^(?:left|center|right)/ ) { |
||
| 72 | _warn( "Invalid align_title specification: " . |
||
| 73 | "'$align_title', using 'left'", |
||
| 74 | ); |
||
| 75 | $align_title = 'left'; |
||
| 76 | } |
||
| 77 | defined $align_title_lines and length $align_title_lines or |
||
| 78 | $align_title_lines = $align_title; |
||
| 79 | unless ( $align_title_lines =~ /^(?:left|center|right)/ ) { |
||
| 80 | _warn( "Invalid align_title_lines specification: " . |
||
| 81 | "'$align_title_lines', using 'left'", |
||
| 82 | ); |
||
| 83 | $align_title_lines = 'left'; |
||
| 84 | } |
||
| 85 | { |
||
| 86 | title => $title, |
||
| 87 | align => $align, |
||
| 88 | align_title => $align_title, |
||
| 89 | align_title_lines => $align_title_lines, |
||
| 90 | sample => $sample, |
||
| 91 | } |
||
| 92 | } |
||
| 93 | |||
| 94 | ### table creation |
||
| 95 | |||
| 96 | sub new { |
||
| 97 | my $tb = bless {}, shift; |
||
| 98 | $tb->_entitle( @_); |
||
| 99 | } |
||
| 100 | |||
| 101 | sub _entitle { |
||
| 102 | my $tb = shift; # will be completely overwritten |
||
| 103 | # find active separators and, well separate them from col specs. |
||
| 104 | # n+1 separators for n cols |
||
| 105 | my ( @seps, @spec); # separators and column specifications |
||
| 106 | my $sep; |
||
| 107 | for ( @_ ) { |
||
| 108 | if ( _is_sep ( $_) ) { |
||
| 109 | $sep = _parse_sep( $_); |
||
| 110 | } else { |
||
| 111 | push @seps, $sep; |
||
| 112 | push @spec, _parse_spec( $_); |
||
| 113 | undef $sep; |
||
| 114 | } |
||
| 115 | } |
||
| 116 | push @seps, $sep; |
||
| 117 | # build sprintf formats from separators |
||
| 118 | my $title_form = |
||
| 119 | _compile_format( map defined() ? $_->{ title} : undef, @seps); |
||
| 120 | my $body_form = |
||
| 121 | _compile_format( map defined() ? $_->{ body} : undef, @seps); |
||
| 122 | |||
| 123 | # pre_align titles |
||
| 124 | my @titles = map [ @{ $_->{ title}}], @spec; |
||
| 125 | my $title_height = 0; |
||
| 126 | _to_max( $title_height, scalar @$_) for @titles; |
||
| 127 | push @$_, ( '') x ( $title_height - @$_) for @titles; |
||
| 128 | # align( 'left', @$_) for @titles; # ready for use' |
||
| 129 | my @styles = map $_->{ align_title_lines}, @spec; |
||
| 130 | align( shift @styles, @$_) for @titles; # in-place alignment |
||
| 131 | |||
| 132 | # build data structure |
||
| 133 | %$tb = ( |
||
| 134 | spec => \ @spec, # column spec for reuse |
||
| 135 | titles => \ @titles, # titles, pre-aligned |
||
| 136 | cols => [ map [], 1 .. @spec], # data columns |
||
| 137 | forms => [ $title_form, $body_form], # separators condensed |
||
| 138 | ); |
||
| 139 | $tb->_clear_cache; |
||
| 140 | } |
||
| 141 | |||
| 142 | # sprintf-format for line assembly, using separators |
||
| 143 | sub _compile_format { |
||
| 144 | my @seps = @_; # mix of strings and undef (for default) |
||
| 145 | defined or $_ = '' for @seps[ 0, -1]; # first and last default to empty |
||
| 146 | defined or $_ = ' ' for @seps; # others default to single space |
||
| 147 | s/%/%%/g for @seps; # protect against sprintf |
||
| 148 | join '%s', @seps; |
||
| 149 | } |
||
| 150 | |||
| 151 | # reverse format compilation (used by colrange()) |
||
| 152 | sub _recover_separators { |
||
| 153 | my $format = shift; |
||
| 154 | my @seps = split /(?<!%)%s/, $format, -1; |
||
| 155 | s/%%/%/g for @seps; |
||
| 156 | @seps; |
||
| 157 | } |
||
| 158 | |||
| 159 | # select some columns, (optionally if in [...]), and add new separators |
||
| 160 | # (the other table creator) |
||
| 161 | sub select { |
||
| 162 | my $tb = shift; |
||
| 163 | my @args = map $tb->_select_group( $_), @_; |
||
| 164 | # get column selection, checking indices (some have been checked by |
||
| 165 | # _select_group, but not all) |
||
| 166 | my @sel = map $tb->_check_index( $_), grep !_is_sep( $_), @args; |
||
| 167 | # replace indices with column spec to create subtable |
||
| 168 | ! _is_sep( $_) and $_ = $tb->{ spec}->[ $_] for @args; |
||
| 169 | my $sub = ref( $tb)->new( @args); |
||
| 170 | # sneak in data columns |
||
| 171 | @{ $sub->{ cols}} = map [ @$_ ], @{ $tb->{ cols}}[ @sel]; |
||
| 172 | $sub; |
||
| 173 | } |
||
| 174 | |||
| 175 | # the first non-separator column in the group is tested if it has any data |
||
| 176 | # if so, the group is returned, else nothing |
||
| 177 | sub _select_group { |
||
| 178 | my ( $tb, $group) = @_; |
||
| 179 | return $_ unless ref $group eq 'ARRAY'; |
||
| 180 | for ( @$group ) { |
||
| 181 | next if _is_sep( $_); |
||
| 182 | $tb->_check_index( $_); |
||
| 183 | return @$group if grep $_, @{ $tb->{ cols}->[ $_]}; |
||
| 184 | return; # no more tries after non-sep was found |
||
| 185 | } |
||
| 186 | return; # no column index in group, no select |
||
| 187 | } |
||
| 188 | |||
| 189 | # check index for validity, return arg if returns at all |
||
| 190 | sub _check_index { |
||
| 191 | my $tb = shift; |
||
| 192 | my ( $i) = @_; |
||
| 193 | my $n = $tb->n_cols; |
||
| 194 | my $ok = eval { |
||
| 195 | use warnings FATAL => 'numeric'; |
||
| 196 | -$n <= $i and $i < $n; # in range of column array? |
||
| 197 | }; |
||
| 198 | _warn( "Invalid column index '$_'") if $@ or not $ok; |
||
| 199 | shift; |
||
| 200 | } |
||
| 201 | |||
| 202 | ### data entry |
||
| 203 | |||
| 204 | sub _clear_cache { @{ $_[ 0]}{ qw( lines blank)} = (); $_[ 0] } |
||
| 205 | |||
| 206 | # add one data line or split the line into follow-up lines |
||
| 207 | sub add { |
||
| 208 | my $tb = shift; |
||
| 209 | $tb->_entitle( ( '') x @_) unless $tb->n_cols; |
||
| 210 | |||
| 211 | $tb->_add( @$_) for _transpose( map [ defined() ? split( $/ ) : '' ], @_); |
||
| 212 | $tb->_clear_cache; |
||
| 213 | } |
||
| 214 | |||
| 215 | # add one data line |
||
| 216 | sub _add { |
||
| 217 | my $tb = shift; |
||
| 218 | push @$_, shift for @{ $tb->{ cols}}; |
||
| 219 | $tb->_clear_cache; |
||
| 220 | $tb; |
||
| 221 | } |
||
| 222 | |||
| 223 | # add one or more data lines |
||
| 224 | sub load { |
||
| 225 | my $tb = shift; |
||
| 226 | for ( @_ ) { |
||
| 227 | defined $_ or $_ = ''; |
||
| 228 | ref eq 'ARRAY' ? $tb->add( @$_) : $tb->add( split); |
||
| 229 | } |
||
| 230 | $tb; |
||
| 231 | } |
||
| 232 | |||
| 233 | sub clear { |
||
| 234 | my $tb = shift; |
||
| 235 | $_ = [] for @{ $tb->{ cols}}; |
||
| 236 | $tb->_clear_cache; |
||
| 237 | $tb; |
||
| 238 | } |
||
| 239 | |||
| 240 | ### access to output area |
||
| 241 | |||
| 242 | ## sizes |
||
| 243 | |||
| 244 | # number of table clolumns |
||
| 245 | sub n_cols { scalar @{ $_[0]->{ spec}} } |
||
| 246 | |||
| 247 | # number of title lines |
||
| 248 | sub title_height { $_[ 0]->n_cols and scalar @{ $_[ 0]->{ titles}->[ 0]} } |
||
| 249 | |||
| 250 | # number of data lines |
||
| 251 | sub body_height { $_[ 0]->n_cols and scalar @{ $_[ 0]->{ cols}->[ 0]} } |
||
| 252 | |||
| 253 | # total height |
||
| 254 | sub table_height { $_[ 0]->title_height + $_[ 0]->body_height } |
||
| 255 | BEGIN { *height = \ &table_height} # alias |
||
| 256 | |||
| 257 | # number of characters in each table line. need to build the table to know |
||
| 258 | sub width { |
||
| 259 | $_[ 0]->height and length( ($_[ 0]->table( 0))[ 0]) - 1; |
||
| 260 | } |
||
| 261 | |||
| 262 | # start and width of each column |
||
| 263 | sub colrange { |
||
| 264 | my ( $tb, $col_index) = @_; |
||
| 265 | return ( 0, 0) unless $tb->width; # width called, $tb->{ blank} exists now |
||
| 266 | $col_index ||= 0; |
||
| 267 | $col_index += $tb->n_cols if $col_index < 0; |
||
| 268 | _to_max( $col_index, 0); |
||
| 269 | _to_min( $col_index, $tb->n_cols); |
||
| 270 | my @widths = map length, @{ $tb->{ blank}}, ''; |
||
| 271 | @widths = @widths[ 0 .. $col_index]; |
||
| 272 | my $width = pop @widths; |
||
| 273 | my $pos = 0; |
||
| 274 | $pos += $_ for @widths; |
||
| 275 | my @seps = _recover_separators( $tb->{ forms}->[ 0]); |
||
| 276 | $pos += length for @seps[ 0 .. $col_index]; |
||
| 277 | return ( $pos, $width); |
||
| 278 | } |
||
| 279 | |||
| 280 | ## printable output |
||
| 281 | |||
| 282 | # whole table |
||
| 283 | sub table { |
||
| 284 | my $tb = shift; |
||
| 285 | $tb->_table_portion( $tb->height, 0, @_); |
||
| 286 | } |
||
| 287 | |||
| 288 | # only titles |
||
| 289 | sub title { |
||
| 290 | my $tb = shift; |
||
| 291 | $tb->_table_portion( $tb->title_height, 0, @_); |
||
| 292 | } |
||
| 293 | |||
| 294 | # only body |
||
| 295 | sub body { |
||
| 296 | my $tb = shift; |
||
| 297 | $tb->_table_portion( $tb->body_height, $tb->title_height, @_); |
||
| 298 | } |
||
| 299 | |||
| 300 | sub stringify { scalar shift()->table() } |
||
| 301 | |||
| 302 | ### common internals |
||
| 303 | |||
| 304 | # common representation of table(), title() and body() |
||
| 305 | sub _table_portion { |
||
| 306 | my $tb = shift; |
||
| 307 | my ( $total, $offset) = ( shift, shift); |
||
| 308 | my ( $from, $n) = ( 0, $total); # if no parameters |
||
| 309 | if ( @_ ) { |
||
| 310 | $from = shift; |
||
| 311 | $n = @_ ? shift : 1; # one line if not given |
||
| 312 | } |
||
| 313 | ( $from, $n) = _limit_range( $total, $from, $n); |
||
| 314 | my @lines = do { |
||
| 315 | my $limit = $tb->title_height; # title format below |
||
| 316 | $from += $offset; |
||
| 317 | map $tb->_assemble_line( $_ >= $limit, $tb->_table_line( $_)), |
||
| 318 | $from .. $from + $n - 1; |
||
| 319 | }; |
||
| 320 | return @lines if wantarray; |
||
| 321 | return join '', @lines; |
||
| 322 | } |
||
| 323 | |||
| 324 | sub _limit_range { |
||
| 325 | my ( $total, $from, $n) = @_; |
||
| 326 | $from ||= 0; |
||
| 327 | $from += $total if $from < 0; |
||
| 328 | $n = $total unless defined $n; |
||
| 329 | return ( 0, 0) if $from + $n < 0 or $from >= $total; |
||
| 330 | $from = 0 if $from < 0; |
||
| 331 | $n = $total - $from if $n > $total - $from; |
||
| 332 | ( $from, $n); |
||
| 333 | } |
||
| 334 | |||
| 335 | # get table line (formatted, including titles). fill cache if needed |
||
| 336 | sub _table_line { |
||
| 337 | my $tb = shift; |
||
| 338 | ($tb->{ lines} ||= [ $tb->_build_table_lines])->[ shift]; |
||
| 339 | } |
||
| 340 | |||
| 341 | # build array of lines of justified data items |
||
| 342 | sub _build_table_lines { |
||
| 343 | my $tb = shift; |
||
| 344 | |||
| 345 | # copy data columns, replacing undef with '' |
||
| 346 | my @cols = map [ map defined() ? $_ : '', @$_], @{ $tb->{ cols}}; |
||
| 347 | |||
| 348 | # add set of empty strings for blank line (needed to build horizontal rules) |
||
| 349 | push @$_, '' for @cols; |
||
| 350 | |||
| 351 | # add samples for minimum alignment |
||
| 352 | my @samples = map $_->{ sample}, @{ $tb->{ spec}}; |
||
| 353 | push @$_, @{ shift @samples} for @cols; |
||
| 354 | |||
| 355 | # align to style |
||
| 356 | my @styles = map $_->{ align}, @{ $tb->{ spec}}; |
||
| 357 | align( shift @styles, @$_) for @cols; |
||
| 358 | # trim off samples, but leave blank line |
||
| 359 | splice @$_, 1 + $tb->body_height for @cols; # + 1 for blank line (brittle) |
||
| 360 | |||
| 361 | # include titles |
||
| 362 | my @titles = @{ $tb->{ titles}}; |
||
| 363 | unshift @$_, @{ shift @titles} for @cols; # add pre-aligned titles |
||
| 364 | |||
| 365 | # align title and body portions of columns |
||
| 366 | # blank line will be there even with no data |
||
| 367 | @styles = map $_->{ align_title}, @{ $tb->{ spec}}; |
||
| 368 | align( shift @styles, @$_) for @cols; # in-place alignment |
||
| 369 | |||
| 370 | # deposit a blank line, pulling it off the columns. |
||
| 371 | # *_rule() knows this is done |
||
| 372 | $tb->{ blank} = [ map pop @$_, @cols]; |
||
| 373 | |||
| 374 | _transpose_n( $tb->height, @cols); # bye-bye, @cols |
||
| 375 | } |
||
| 376 | |||
| 377 | # destructively transpose a number of lines/cols from an array of arrayrefs |
||
| 378 | sub _transpose_n ($@) { |
||
| 379 | my $n = shift; |
||
| 380 | map [ map shift @$_, @_], 1 .. $n; |
||
| 381 | } |
||
| 382 | |||
| 383 | # like _transpose_n, but find the number to transpose from max of given |
||
| 384 | sub _transpose { |
||
| 385 | my $m; |
||
| 386 | _to_max( $m, scalar @$_) for @_, []; # make sure $m is defined |
||
| 387 | _transpose_n( $m, @_); |
||
| 388 | } |
||
| 389 | |||
| 390 | # make a line from a number of formatted data elements |
||
| 391 | sub _assemble_line { |
||
| 392 | my $tb = shift; |
||
| 393 | my $in_body = shift; # 0 for title, 1 for body |
||
| 394 | sprintf( $tb->{ forms}->[ !!$in_body], @{ shift()}) . "\n"; |
||
| 395 | } |
||
| 396 | |||
| 397 | # build a rule line |
||
| 398 | sub _rule { |
||
| 399 | my $tb = shift; |
||
| 400 | my $in_body = shift; |
||
| 401 | return '' unless $tb->width; # this builds the cache, hence $tb->{ blank} |
||
| 402 | my $rule = $tb->_assemble_line( $in_body, $tb->{ blank}); |
||
| 403 | my ( $char, $alt) = map /(.)/, @_; |
||
| 404 | ( defined $char and length $char) or $char = ' '; |
||
| 405 | # replace blanks with $char. If $alt is given, replace nonblanks with $alt |
||
| 406 | if ( defined $alt ) { |
||
| 407 | $rule =~ s/(.)/$1 eq ' ' ? $char : $alt/ge; |
||
| 408 | } else { |
||
| 409 | $rule =~ s/ /$char/g if $char ne ' '; |
||
| 410 | } |
||
| 411 | $rule; |
||
| 412 | } |
||
| 413 | |||
| 414 | sub rule { |
||
| 415 | my $tb = shift; |
||
| 416 | $tb->_rule( 0, @_); |
||
| 417 | } |
||
| 418 | |||
| 419 | sub body_rule { |
||
| 420 | my $tb = shift; |
||
| 421 | $tb->_rule( 1, @_); |
||
| 422 | } |
||
| 423 | |||
| 424 | # min/max utilitiess (modifying first argument) |
||
| 425 | |||
| 426 | sub _to_max { |
||
| 427 | defined $_[ 0] and $_[ 0] > $_[ 1] or $_[ 0] = $_[ 1] if defined $_[ 1]; |
||
| 428 | } |
||
| 429 | |||
| 430 | sub _to_min { |
||
| 431 | defined $_[ 0] and $_[ 0] < $_[ 1] or $_[ 0] = $_[ 1] if defined $_[ 1]; |
||
| 432 | } |
||
| 433 | |||
| 434 | ### warning behavior |
||
| 435 | use Carp; |
||
| 436 | |||
| 437 | { |
||
| 438 | my ( $warn, $fatal) = ( 0, 0); |
||
| 439 | |||
| 440 | sub warnings { |
||
| 441 | shift; # ignore class/object |
||
| 442 | local $_ = shift || 'on'; |
||
| 443 | if ( $_ eq 'off' ) { |
||
| 444 | ( $warn, $fatal) = ( 0, 0); |
||
| 445 | } elsif ( $_ eq 'fatal' ) { |
||
| 446 | ( $warn, $fatal) = ( 1, 1); |
||
| 447 | } else { |
||
| 448 | ( $warn, $fatal) = ( 1, 0); |
||
| 449 | } |
||
| 450 | return 'fatal' if $fatal; |
||
| 451 | return 'on' if $warn; |
||
| 452 | return 'off'; |
||
| 453 | } |
||
| 454 | |||
| 455 | sub _warn { |
||
| 456 | my $msg = shift; |
||
| 457 | return unless $warn; |
||
| 458 | croak( $msg) if $fatal; |
||
| 459 | carp( $msg); |
||
| 460 | } |
||
| 461 | } |
||
| 462 | |||
| 463 | __END__ |
||
| 464 | ########################################### main pod documentation begin ## |
||
| 465 | |||
| 466 | =head1 NAME |
||
| 467 | |||
| 468 | Text::Table - Organize Data in Tables |
||
| 469 | |||
| 470 | =head1 SYNOPSIS |
||
| 471 | |||
| 472 | use Text::Table; |
||
| 473 | my $tb = Text::Table->new( |
||
| 474 | "Planet", "Radius\nkm", "Density\ng/cm^3" |
||
| 475 | ); |
||
| 476 | $tb->load( |
||
| 477 | [ "Mercury", 2360, 3.7 ], |
||
| 478 | [ "Venus", 6110, 5.1 ], |
||
| 479 | [ "Earth", 6378, 5.52 ], |
||
| 480 | [ "Jupiter", 71030, 1.3 ], |
||
| 481 | ); |
||
| 482 | print $tb; |
||
| 483 | |||
| 484 | This prints a table from the given title and data like this: |
||
| 485 | |||
| 486 | Planet Radius Density |
||
| 487 | km g/cm^3 |
||
| 488 | Mercury 2360 3.7 |
||
| 489 | Venus 6110 5.1 |
||
| 490 | Earth 6378 5.52 |
||
| 491 | Jupiter 71030 1.3 |
||
| 492 | |||
| 493 | Note that two-line titles work, and that the planet names are aligned |
||
| 494 | differently than the numbers. |
||
| 495 | |||
| 496 | =head1 DESCRIPTION |
||
| 497 | |||
| 498 | Organization of data in table form is a time-honored and useful |
||
| 499 | method of data representation. While columns of data are trivially |
||
| 500 | generated by computer through formatted output, even simple tasks |
||
| 501 | like keeping titles aligned with the data columns are not trivial, |
||
| 502 | and the one-shot solutions one comes up with tend to be particularly |
||
| 503 | hard to maintain. Text::Table allows you to create and maintain |
||
| 504 | tables that adapt to alignment requirements as you use them. |
||
| 505 | |||
| 506 | =head2 Overview |
||
| 507 | |||
| 508 | The process is simple: you create a table (a Text::Table object) by |
||
| 509 | describing the columns the table is going to have. Then you load |
||
| 510 | lines of data into the table, and finally print the resulting output |
||
| 511 | lines. Alignment of data and column titles is handled dynamically |
||
| 512 | in dependence on the data present. |
||
| 513 | |||
| 514 | =head2 Table Creation |
||
| 515 | |||
| 516 | In the simplest case, if all you want is a number of (untitled) columns, |
||
| 517 | you create an unspecified table and start adding data to it. The number |
||
| 518 | of columns is taken fronm the first line of data. |
||
| 519 | |||
| 520 | To specify a table you specify its columns. A column description |
||
| 521 | can contain a title and alignment requirements for the data, both |
||
| 522 | optional. Additionally, you can specify how the title is aligned with |
||
| 523 | the body of a column, and how the lines of a multiline title are |
||
| 524 | aligned among themselves. |
||
| 525 | |||
| 526 | The columns are collected in the table in the |
||
| 527 | order they are given. On data entry, each column corresponds to |
||
| 528 | one data item, and in column selection columns are indexed left to |
||
| 529 | right, starting from 0. |
||
| 530 | |||
| 531 | Each title can be a multiline string which will be blank-filled to |
||
| 532 | the length of the longest partial line. The largest number of title |
||
| 533 | lines in a column determines how many title lines the table has as a |
||
| 534 | whole, including the case that no column has any titles. |
||
| 535 | |||
| 536 | On output, Columns are separated by a single blank. You can control |
||
| 537 | what goes between columns by specifying separators between (or before, |
||
| 538 | or after) columns. Separators don't contain any data and don't count |
||
| 539 | in column indexing. They also don't accumulate: in a sequence of only |
||
| 540 | separators and no columns, only the last one counts. |
||
| 541 | |||
| 542 | =head2 Status Information |
||
| 543 | |||
| 544 | The width (in characters), height (in lines), number of columns, and |
||
| 545 | similar data about the table is available. |
||
| 546 | |||
| 547 | =head2 Data Loading |
||
| 548 | |||
| 549 | Table data is entered line-wise, each time specifying data entries |
||
| 550 | for all table columns. A bulk loader for many lines at once is also |
||
| 551 | available. You can clear the data from the table for re-use (though |
||
| 552 | you will more likely just create another table). |
||
| 553 | |||
| 554 | =head2 Table Output |
||
| 555 | |||
| 556 | The output area of a table is divided in the title and the body. |
||
| 557 | |||
| 558 | The title contains the combined titles from the table columns, if |
||
| 559 | any. Its content never changes with a given table, but it may be |
||
| 560 | spread out differently on the page through alignment with the data. |
||
| 561 | |||
| 562 | The body contains the data lines, aligned column-wise as specified, |
||
| 563 | and left-aligned with the column title. |
||
| 564 | |||
| 565 | Each of these is arranged like a Perl array (counting from 0) and can |
||
| 566 | be accessed in portions by specifying a first line and the number |
||
| 567 | of following lines. Also like an array, giving a negative first line |
||
| 568 | counts from the end of the area. The whole table, the title followed |
||
| 569 | by the body, can also be accessed in this manner. |
||
| 570 | |||
| 571 | The subdivisions are there so you can repeat the title (or parts of |
||
| 572 | it) along with parts of the body on output, whether for screen paging |
||
| 573 | or printout. |
||
| 574 | |||
| 575 | A rule line is also available, which is the horizontal counterpart to |
||
| 576 | the separator columns you specify with the table. |
||
| 577 | It is basically a table line as it would appear if all data entries |
||
| 578 | in the line were empty, that is, a blank line except for where the |
||
| 579 | column separators have non-blank entries. If you print it between |
||
| 580 | data lines, it will not disrupt the vertical separator structure |
||
| 581 | as a plain blank line would. You can also request a solid rule |
||
| 582 | consisting of any character, and even one with the non-blank column |
||
| 583 | separators replaced by a character of your choice. This way you can |
||
| 584 | get the popular representation of line-crossings like so: |
||
| 585 | |||
| 586 | | |
||
| 587 | ----+--- |
||
| 588 | | |
||
| 589 | |||
| 590 | =head2 Warning Control |
||
| 591 | |||
| 592 | On table creation, some parameters are checked and warnings issued |
||
| 593 | if you allow warnings. You can also turn warnings into fatal errors. |
||
| 594 | |||
| 595 | =head1 SPECIFICATIONS |
||
| 596 | |||
| 597 | =head2 Column Specification |
||
| 598 | |||
| 599 | Each column specification is a single scalar. Columns can be either proper |
||
| 600 | data columns or column separators. Both can be specified either as |
||
| 601 | (possibly multi-line) strings, or in a more explicit form as hash-refs. |
||
| 602 | In the string form, proper columns are given as plain strings, and |
||
| 603 | separators are given as scalar references to strings. In hash form, |
||
| 604 | separators have a true value in the field C<is_sep> while proper columns |
||
| 605 | don't have this field. |
||
| 606 | |||
| 607 | =over 4 |
||
| 608 | |||
| 609 | =item Columns as strings |
||
| 610 | |||
| 611 | A column is given as a column title (any number of lines), |
||
| 612 | optionally followed by alignment requirements. Alignment requirements |
||
| 613 | start with a line that begins with an ampersamd "&". However, only the |
||
| 614 | last such line counts as such, so if you have title lines that begin |
||
| 615 | with "&", just append an ampersand on a line by itself as a dummy |
||
| 616 | alignment section if you don't have one anyway. |
||
| 617 | |||
| 618 | What follows the ampersand on its line is the alignment style (like |
||
| 619 | I<left>, I<right>, ... as described in L<"Alignment">), you want for |
||
| 620 | the data in this column. If nothing follows, the general default I<auto> |
||
| 621 | is used. If you specify an invalid alignment style, it falls back to |
||
| 622 | left alignment. |
||
| 623 | |||
| 624 | The lines that follow can contain sample data for this column. These |
||
| 625 | are considered for alignment in the column, but never actually appear |
||
| 626 | in the output. The effect is to guarantee a minimum width for the |
||
| 627 | column even if the current data doesn't require it. This helps dampen |
||
| 628 | the oscillations in the appearance of dynamically aligned tables. |
||
| 629 | |||
| 630 | =item Columns as Hashes |
||
| 631 | |||
| 632 | The format is |
||
| 633 | |||
| 634 | { |
||
| 635 | title => $title, |
||
| 636 | align => $align, |
||
| 637 | sample => $sample, |
||
| 638 | align_title => $align_title, |
||
| 639 | align_title_lines => $align_title_lines, |
||
| 640 | } |
||
| 641 | |||
| 642 | $title contains the title lines and $sample the sample data. Both can |
||
| 643 | be given as a string or as an array-ref to the list of lines. $align contains |
||
| 644 | the alignment style (without a leading ampersand), usually as a string. |
||
| 645 | You can also give a regular expression here, which specifies regex alignment. |
||
| 646 | A regex can only be specified in the hash form of a colunm specification. |
||
| 647 | |||
| 648 | In hash form you can also specify how the title of a column is aligned |
||
| 649 | with its body. To do this, you specify the keyword C<align_title> with |
||
| 650 | C<left>, C<right> or C<center>. Other alignment specifications are not |
||
| 651 | valid here. The default is C<left>. |
||
| 652 | |||
| 653 | C<align_title> also specifies how the lines of a multiline title are |
||
| 654 | aligned among themselves. If you want a different alignment, you |
||
| 655 | can specify it with the key C<align_title_lines>. Again, only C<left>, |
||
| 656 | C<right> or C<center> are allowed. |
||
| 657 | |||
| 658 | Do not put other keys than those mentioned above (I<title>, I<align>, |
||
| 659 | I<align_title>, I<align_title_lines>, and I<sample>) into a hash that |
||
| 660 | specifies a column. Most would be ignored, but some would confuse the |
||
| 661 | interpreter (in particular, I<is_sep> has to be avoided). |
||
| 662 | |||
| 663 | =item Separators as strings |
||
| 664 | |||
| 665 | A separator must be given as a reference to a string (often a literal, |
||
| 666 | like C<\' | '>), any string that is given directly describes a column. |
||
| 667 | |||
| 668 | It is usually just a (short) string that will be printed between |
||
| 669 | table columns on all table lines instead of the default single |
||
| 670 | blank. If you specify two separators (on two lines), the first one |
||
| 671 | will be used in the title and the other in the body of the table. |
||
| 672 | |||
| 673 | =item Separators as Hashes |
||
| 674 | |||
| 675 | The hash representation of a separator has the format |
||
| 676 | |||
| 677 | { |
||
| 678 | is_sep => 1, |
||
| 679 | title => $title, |
||
| 680 | body => $body, |
||
| 681 | } |
||
| 682 | |||
| 683 | $title is the separator to be used in the title area and $body |
||
| 684 | the one for the body. If only one is given, the other is used for |
||
| 685 | both. If none is given, a blank is used. If one is shorter than |
||
| 686 | the other, it is blank filled on the right. |
||
| 687 | |||
| 688 | The value of C<is_sep> must be set to a true value, this is the |
||
| 689 | distinguishing feature of a separator. |
||
| 690 | |||
| 691 | =back |
||
| 692 | |||
| 693 | =head2 Alignment |
||
| 694 | |||
| 695 | The original documentation to L<Text::Aligner> contains all the details |
||
| 696 | on alignment specification, but here is the rundown: |
||
| 697 | |||
| 698 | The possible alignment specifications are I<left>, I<right>, I<center>, |
||
| 699 | I<num> and I<point> (which are synonyms), and I<auto>. The first |
||
| 700 | three explain themselves. |
||
| 701 | |||
| 702 | I<num> (and I<point>) align the decimal point in the data, which is |
||
| 703 | assumed to the right if none is present. Strings that aren't |
||
| 704 | numbers are treated the same way, that is, they appear aligned |
||
| 705 | with the integers unless they contain a ".". Instead of the |
||
| 706 | decimal point ".", you can also specify any other string in |
||
| 707 | the form I<num(,)>, for instance. The string in parentheses |
||
| 708 | is aligned in the data. The synonym I<point> for I<num> may be |
||
| 709 | more appropriate in contexts that deal with arbitrary |
||
| 710 | strings, as in I<point(=E<gt>)> (which might be used to align certain |
||
| 711 | bits of Perl code). |
||
| 712 | |||
| 713 | I<regex alignment> is a more sophisticated form of point alignment. |
||
| 714 | If you specify a regular expression, as delivered by C<qr//>, the start |
||
| 715 | of the match is used as the alignment point. If the regex contains |
||
| 716 | capturing parentheses, the last submatch counts. [The usefulness of |
||
| 717 | this feature is under consideration.] |
||
| 718 | |||
| 719 | I<auto> alignment combines numeric alignment with left alignment. |
||
| 720 | Data items that look like numbers, and those that don't, form two |
||
| 721 | virtual columns and are aligned accordingly: C<num> for numbers and |
||
| 722 | C<left> for other strings. These columns are left-aligned with |
||
| 723 | each other (i.e. the narrower one is blank-filled) to form the |
||
| 724 | final alignment. |
||
| 725 | |||
| 726 | This way, a column that happens to have only numbers in the data gets |
||
| 727 | I<num> alignment, a column with no numbers appears I<left>-aligned, |
||
| 728 | and mixed data is presented in a reasonable way. |
||
| 729 | |||
| 730 | =head2 Column Selection |
||
| 731 | |||
| 732 | Besides creating tables from scratch, they can be created by |
||
| 733 | selecting columns from an existing table. Tables created this |
||
| 734 | way contain the data from the columns they were built from. |
||
| 735 | |||
| 736 | This is done by specifying the columns to select by their index |
||
| 737 | (where negative indices count backward from the last column). |
||
| 738 | The same column can be selected more than once and the sequence |
||
| 739 | of columns can be arbitrarily changed. Separators don't travel |
||
| 740 | with columns, but can be specified between the columns at selection |
||
| 741 | time. |
||
| 742 | |||
| 743 | You can make the selection of one or more columns dependent on |
||
| 744 | the data content of one of them. If you specify some of the columns |
||
| 745 | in angle brackets [...], the whole group is only included in the |
||
| 746 | selection if the first column in the group contains any data that |
||
| 747 | evaluates to boolean true. That way you can de-select parts of a |
||
| 748 | table if it contains no interesting data. Any column separators |
||
| 749 | given in brackets are selected or deselected along with the rest |
||
| 750 | of it. |
||
| 751 | |||
| 752 | =head1 PUBLIC METHODS |
||
| 753 | |||
| 754 | =head2 Table Creation |
||
| 755 | |||
| 756 | =over 4 |
||
| 757 | |||
| 758 | =item new() |
||
| 759 | |||
| 760 | my $tb = Text::Table->new( $column, ... ); |
||
| 761 | |||
| 762 | creates a table with the columns specified. A column can be proper column |
||
| 763 | which contains and displays data, or a separator which tells how to fill |
||
| 764 | the space between columns. The format of the parameters is described under |
||
| 765 | L<"Column Specification">. Specifying an invalid alignment for a column |
||
| 766 | results in a warning if these are allowed. |
||
| 767 | |||
| 768 | If no columns are specified, the number of columns is taken from the first |
||
| 769 | line of data added to the table. The effect is as if you had specified |
||
| 770 | C<Text::Table-E<gt>new( ( '') x $n)>, where C<$n> is the number of |
||
| 771 | columns. |
||
| 772 | |||
| 773 | =item select() |
||
| 774 | |||
| 775 | my $sub = $tb->select( $column, ...); |
||
| 776 | |||
| 777 | creates a table from the listed columns of the table $tb, including |
||
| 778 | the data. Columns are specified as integer indices which refer to |
||
| 779 | the data columns of $tb. Columns can be repeated and specified in any |
||
| 780 | order. Negative indices count from the last column. If an invalid |
||
| 781 | index is specified, a warning is issued, if allowed. |
||
| 782 | |||
| 783 | As with L<"new()">, separators can be interspersed among the column |
||
| 784 | indices and will be used between the columns of the new table. |
||
| 785 | |||
| 786 | If you enclose some of the arguments (column indices or separators) in |
||
| 787 | angle brackets C<[...]> (technically, you specify them inside an |
||
| 788 | arrayref), they form a group for conditional selection. The group is |
||
| 789 | only included in the resulting table if the first actual column inside |
||
| 790 | the group contains any data that evaluate to a boolean true. This way |
||
| 791 | you can exclude groups of columns that wouldn't contribute anything |
||
| 792 | interesting. Note that separators are selected and de-selected with |
||
| 793 | their group. That way, more than one separator can appear between |
||
| 794 | adjacent columns. They don't add up, but only the rightmost separator |
||
| 795 | is used. A group that contains only separators is never selected. |
||
| 796 | [Another feature whose usefulness is under consideration.] |
||
| 797 | |||
| 798 | =back |
||
| 799 | |||
| 800 | =head2 Status Information |
||
| 801 | |||
| 802 | =over 4 |
||
| 803 | |||
| 804 | =item n_cols() |
||
| 805 | |||
| 806 | $tb->n_cols |
||
| 807 | |||
| 808 | returns the number of columns in the table. |
||
| 809 | |||
| 810 | =item width() |
||
| 811 | |||
| 812 | $tb->width |
||
| 813 | |||
| 814 | returns the width (in characters) of the table. All table lines have |
||
| 815 | this length (not counting a final "\n" in the line), as well as the |
||
| 816 | separator lines returned by $tb->rule() and $b->body_rule(). |
||
| 817 | The width of a table can potentially be influenced by any data item |
||
| 818 | in it. |
||
| 819 | |||
| 820 | =item height() |
||
| 821 | |||
| 822 | $tb->height |
||
| 823 | |||
| 824 | returns the total number of lines in a table, including title lines |
||
| 825 | and body lines. For orthogonality, the synonym table_height() also |
||
| 826 | exists. |
||
| 827 | |||
| 828 | =item title_height() |
||
| 829 | |||
| 830 | $tb->title_height |
||
| 831 | |||
| 832 | returns the number of title lines in a table. |
||
| 833 | |||
| 834 | =item body_height() |
||
| 835 | |||
| 836 | $tb->body_height |
||
| 837 | |||
| 838 | returns the number of lines in the table body. |
||
| 839 | |||
| 840 | =item colrange() |
||
| 841 | |||
| 842 | $tb->colrange( $i) |
||
| 843 | |||
| 844 | returns the start position and width of the $i-th column (counting from 0) |
||
| 845 | of the table. If $i is negative, counts from the end of the table. If $i |
||
| 846 | is larger than the greatest column index, an imaginary column of width 0 |
||
| 847 | is assumed right of the table. |
||
| 848 | |||
| 849 | =back |
||
| 850 | |||
| 851 | =head2 Data Loading |
||
| 852 | |||
| 853 | =over 4 |
||
| 854 | |||
| 855 | =item add() |
||
| 856 | |||
| 857 | $tb->add( $col1, ..., $colN) |
||
| 858 | |||
| 859 | adds a data line to the table, returns the table. |
||
| 860 | |||
| 861 | C<$col1>, ..., C<$colN> are scalars that |
||
| 862 | correspond to the table columns. Undefined entries are converted to '', |
||
| 863 | and extra data beyond the number of table columns is ignored. |
||
| 864 | |||
| 865 | Data entries can be multi-line strings. The partial strings all go into |
||
| 866 | the same column. The corresponding fields of other columns remain empty |
||
| 867 | unless there is another multi-line entry in that column that fills the |
||
| 868 | fieds. Adding a line with multi-line entries is equivalent to adding |
||
| 869 | multiple lines. |
||
| 870 | |||
| 871 | Every call to C<add()> increases the body height of the table by the |
||
| 872 | number of effective lines, one in the absence of multiline entries. |
||
| 873 | |||
| 874 | =item load() |
||
| 875 | |||
| 876 | $tb->load( $line, ...) |
||
| 877 | |||
| 878 | loads the data lines given into the table, returns the table. |
||
| 879 | |||
| 880 | Every argument to C<load()> represents a data line to be added to the |
||
| 881 | table. The line can be given as an array(ref) containing the data |
||
| 882 | items, or as a string, which is split on whitespace to retrieve the |
||
| 883 | data. If an undefined argument is given, it is treated as an |
||
| 884 | empty line. |
||
| 885 | |||
| 886 | =item clear() |
||
| 887 | |||
| 888 | $tb->clear; |
||
| 889 | |||
| 890 | deletes all data from the table and resets it to the state after |
||
| 891 | creation. Returns the table. The body height of a table is 0 after |
||
| 892 | C<clear()>. |
||
| 893 | |||
| 894 | =back |
||
| 895 | |||
| 896 | =head2 Table Output |
||
| 897 | |||
| 898 | The three methods C<table()>, C<title()>, and C<body()> are very similar. |
||
| 899 | They access different parts of the printable output lines of a table with |
||
| 900 | similar methods. The details are described with the C<table()> method. |
||
| 901 | |||
| 902 | =over 4 |
||
| 903 | |||
| 904 | =item table() |
||
| 905 | |||
| 906 | The C<table()> method returns lines from the entire table, starting |
||
| 907 | with the first title line and ending with the last body line. |
||
| 908 | |||
| 909 | In array context, the lines are returned separately, in scalar context |
||
| 910 | they are joined together in a single string. |
||
| 911 | |||
| 912 | my @lines = $tb->table; |
||
| 913 | my $line = $tb->table( $line_number); |
||
| 914 | my @lines = $tb->table( $line_number, $n); |
||
| 915 | |||
| 916 | The first call returns all the lines in the table. The second call |
||
| 917 | returns one line given by $line_number. The third call returns $n |
||
| 918 | lines, starting with $line_number. If $line_number is negative, it |
||
| 919 | counts from the end of the array. Unlike the C<select()> method, |
||
| 920 | C<table()> (and its sister methods C<title()> and C<body()>) is |
||
| 921 | protected against large negative line numbers, it truncates the |
||
| 922 | range described by $line_number and $n to the existing lines. If |
||
| 923 | $n is 0 or negative, no lines are returned (an empty string in scalar |
||
| 924 | context). |
||
| 925 | |||
| 926 | =item title() |
||
| 927 | |||
| 928 | Returns lines from the title area of a table, where the column titles |
||
| 929 | are rendered. Parameters and response to context are as with C<table()>, |
||
| 930 | but no lines are returned from outside the title area. |
||
| 931 | |||
| 932 | =item body() |
||
| 933 | |||
| 934 | Returns lines from the body area of a table, that is the part where |
||
| 935 | the data content is rendered, so that $tb->body( 0) is the first data |
||
| 936 | line. Parameters and response to context are as with C<table()>. |
||
| 937 | |||
| 938 | =item rule() |
||
| 939 | |||
| 940 | $tb->rule; |
||
| 941 | $tb->rule( $char); |
||
| 942 | $tb->rule( $char, $char1); |
||
| 943 | |||
| 944 | Returns a rule for the table. |
||
| 945 | |||
| 946 | A rule is a line of table width that can be used between table lines |
||
| 947 | to provide visual horizontal divisions, much like column separators |
||
| 948 | provide vertical visual divisions. In its basic form (returned by the |
||
| 949 | first call) it looks like a table line with no data, hence a blank |
||
| 950 | line except for the non-blank parts of any column-separators. If |
||
| 951 | one character is specified (the second call), it replaces the blanks |
||
| 952 | in the first form, but non-blank column separators are retained. If |
||
| 953 | a second character is specified, it replaces the non-blank parts of |
||
| 954 | the separators. So specifying the same character twice gives a solid |
||
| 955 | line of table width. Another useful combo is C<$tb-E<lt>rule( '-', '+')>, |
||
| 956 | together with separators that contain a single nonblank "|", for a |
||
| 957 | popular representation of line crossings. |
||
| 958 | |||
| 959 | C<rule()> uses the column separators for the title section if there |
||
| 960 | is a difference. |
||
| 961 | |||
| 962 | =item body_rule() |
||
| 963 | |||
| 964 | C<body_rule()> works like <rule()>, except the rule is generated using |
||
| 965 | the column separators for the table body. |
||
| 966 | |||
| 967 | =back |
||
| 968 | |||
| 969 | =head2 Warning Control |
||
| 970 | |||
| 971 | =over 4 |
||
| 972 | |||
| 973 | =item warnings() |
||
| 974 | |||
| 975 | Text::Table->warnings(); |
||
| 976 | Text::Table->warnings( 'on'); |
||
| 977 | Text::Table->warnings( 'off'): |
||
| 978 | Text::Table->warnings( 'fatal'): |
||
| 979 | |||
| 980 | The C<warnings()> method is used to control the appearance of warning |
||
| 981 | messages while tables are manipulated. When Text::Table starts, warnings |
||
| 982 | are disabled. The default action of C<warnings()> is to turn warnings |
||
| 983 | on. The other possible arguments are self-explanatory. C<warnings()> |
||
| 984 | can also be called as an object method (C<$tb-E<gt>warnings( ...)>). |
||
| 985 | |||
| 986 | =back |
||
| 987 | |||
| 988 | =head1 VERSION |
||
| 989 | |||
| 990 | This document pertains to Text::Table version 1.107 |
||
| 991 | |||
| 992 | =head1 BUGS |
||
| 993 | |||
| 994 | =over 4 |
||
| 995 | |||
| 996 | =item o |
||
| 997 | |||
| 998 | I<auto> alignment doesn't support alternative characters for the decimal |
||
| 999 | point. This is actually a bug in the underlying Text::Aligner by the |
||
| 1000 | same author. |
||
| 1001 | |||
| 1002 | =back |
||
| 1003 | |||
| 1004 | =head1 AUTHOR |
||
| 1005 | |||
| 1006 | Anno Siegel |
||
| 1007 | CPAN ID: ANNO |
||
| 1008 | siegel@zrz.tu-berlin.de |
||
| 1009 | http://www.tu-berlin.de/~siegel |
||
| 1010 | |||
| 1011 | =head1 COPYRIGHT |
||
| 1012 | |||
| 1013 | Copyright (c) 2002 Anno Siegel. All rights reserved. |
||
| 1014 | This program is free software; you can redistribute |
||
| 1015 | it and/or modify it under the same terms as Perl itself. |
||
| 1016 | |||
| 1017 | The full text of the license can be found in the |
||
| 1018 | LICENSE file included with this module. |
||
| 1019 | |||
| 1020 | =head1 SEE ALSO |
||
| 1021 | |||
| 1022 | Text::Aligner, perl(1). |
||
| 1023 | |||
| 1024 | =cut |
||
| 1025 | |||
| 1026 | 1; |