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

Hi, I have searched for a way to underline text on your board and came up with "How do I print characters with underline or bold?" #106173=categorized question: It doesn’t help me for what I want to do, because I am using Perl, version 5.005_01 built for powerpc-machten (BSD 4.4) and I cant use modules. I can’t use MacPerl because I use several switches on the command line when I run the complete program. I’m not a student and this isn’t a homework assignment. I want to underline only the column headings and not the spaces between, that I get from the output of this portion of the program. The underlining varies; as much as 5 in the case of high frequency letters such as 11R13 to 3 for low frequency letters like 5F7. I have been trying "\137" on line 29 which should show as: push @line, sprintf('%2d%s%-2d', scalar @{$trigraphs{$k}}, $k, scalar keys %{$flanks{$k}}); and I can’t get it right. Perhaps my approach is wrong. Thanks for any help.
#! /usr/bin/perl -w use strict; # get cipherText my $txt = do{local $/; <DATA>}; $txt =~ s/\s//go; my @letters = split '', $txt; # create trigraphs my (%trigraphs, %flanks); add_trigraph('-', @letters[0..1]); while(@letters > 2){ add_trigraph(@letters[0..2]); shift @letters; } add_trigraph(@letters[0..1], '-'); # sort by most seen middle character my @sorted_keys = sort{ @{$trigraphs{$b}} <=> @{$trigraphs{$a}} || $a cmp $b } keys %trigraphs; # create columns/lines for(my $i = -1;$i < @{$trigraphs{$sorted_keys[0]}};++$i){ my @line; for my $k (@sorted_keys){ unless($i > -1){ # create header @{$trigraphs{$k}} = sort @{$trigraphs{$k}}; push @line, sprintf('%2d%s%-2d', scalar @{$tri +graphs{$k}}, $k, scalar keys %{$flanks{$k}}); next; } next unless defined $trigraphs{$k}->[$i]; push @line, sprintf(' %s ', $trigraphs{$k}->[$i]); } print "@line\n"; } sub add_trigraph { ++$flanks{$_[1]}->{$_} for @_[0,2]; push(@{$trigraphs{$_[1]}}, join('', @_)); } __DATA__ # elcy pg.73 in P format FDRJN UHVXX URDMD SKVSO PJRKZ DYFZJ XGSRR VTQYR WDARW DFVRK VDRKV TDFSZ ZDYFR DNNVO VTSXS AWVZR

Replies are listed 'Best First'.
Re: underline variable width column headings
by quester (Vicar) on Oct 30, 2006 at 06:39 UTC
    Can you post the code that you were trying to use for underlining...? Incidentally, "\137" or "_" is a separate character which usually can't be used to to overstrike another character on an xterm or similar window. The usual thing to do on a terminal is to print hyphens on a separate line below the characters to be "underlined". That may be your only option if you want five levels of underlining; most printer escape sequences for underlining can only describe single or double underlining.
      Quester, thank you for your reply. Basically, I was trying to insert \137 in various places along line 29 to see what effect it would have. I got underlines before the column headings and I forget where else, but not where I wanted. Because the Perl program that I have does miraculous things, I just assumed I could underline the columns. I wondered why I couldn’t find a regex or a double-quoted string representation.
      I have been able to underline the column headings using Term::ANSIColor but I am not satisfied with the result. There can be twenty six columns depending on the data, so I want to keep my output width to a minimum. The column header consists of one or two digits (the count of the letter) followed by the letter followed by one or two digits called flank in the code (the count of how many times the letter contacts a different letter). The headers vary in width from five to three characters. The line (32) in question is: push @line, sprintf('%2d%s%-2d', scalar @{$trigraphs{$k}}, $k, scalar keys %{$flanks{$k}}); When I use colored in the format portion of sprintf(), like this: sprintf(colored('%2d%s%-2d', 'underline'), I always get an underline that is five characters wide, which is good when my column header is five characters wide; not so good when the header is only three characters wide. I was forced to go that route because even though I got the correct results with the letter part of the header, like this: colored($k, 'underline'), I was unable to get around the fact that I couldn’t use the %d identifier when I tried to underline what I thought would be numerical values from the scalar part of my code (it still sees $k as letters). If I changed %d to %s and used:
      colored(scalar @{$trigraphs{$k}}, 'underline'), and colored(scalar keys %{$flanks{$k}}, 'underline')); Note: the last ) is to match the ( in sprintf().
      I could get the underline I want, but the headers no longer lined up with the letter above it’s corresponding letter in the columns. The columns print as they should with three spaces between them. That is my problem, can anyone help? Should anyone want to run the code
      #! /usr/bin/perl use strict; use warnings; use Term::ANSIColor; # get cipherText my $txt = do{local $/; <DATA>}; $txt =~ s/\s//go; my @letters = split '', $txt; # create trigraphs my (%trigraphs, %flanks); add_trigraph('-', @letters[0..1]); while(@letters > 2){ add_trigraph(@letters[0..2]); shift @letters; } add_trigraph(@letters[0..1], '-'); # sort by most seen middle character my @sorted_keys = sort{ @{$trigraphs{$b}} <=> @{$trigraphs{$a}} || $a cmp $b } keys %trigraphs; # create columns/lines for(my $i = -1;$i < @{$trigraphs{$sorted_keys[0]}};++$i){ my @line; for my $k (@sorted_keys){ unless($i > -1){ # create header @{$trigraphs{$k}} = sort @{$trigraphs{$k}}; push @line, sprintf('%2d%s%-2d', scalar @{$tri +graphs{$k}}, $k, scalar keys %{$flanks{$k}}); next; } next unless defined $trigraphs{$k}->[$i]; push @line, sprintf(' %s ', $trigraphs{$k}->[$i]); } print "@line\n"; } sub add_trigraph { ++$flanks{$_[1]}->{$_} for @_[0,2]; push(@{$trigraphs{$_[1]}}, join('', @_)); } __DATA__ # elcy pg.73 in P format FDRJN UHVXX URDMD SKVSO PJRKZ DYFZJ XGSRR VTQYR WDARW DFVRK VDRKV TDFSZ ZDYFR DNNVO VTSXS AWVZR
        The colored(...) function wants a character string argument, so you must call sprintf first to convert your numbers to strings, then colored. This fix works (at least on Linux):
        push @line, colored(sprintf('%2d', scalar @{$trigraphs{$k}}), 'und +erline') . $k . colored(sprintf('%-2d',scalar keys %{$flanks{$k}}), 'underline') +;
Re: underline variable width column headings
by Anonymous Monk on Oct 30, 2006 at 06:34 UTC
    It doesn’t help me for what I want to do, because I am using Perl, version 5.005_01 built for powerpc-machten (BSD 4.4) and I cant use modules.
    Yes you can.
    # Term::ANSIColor -- Color screen output using ANSI escape sequences. # $Id: ANSIColor.pm 58 2006-07-12 22:30:55Z eagle $ # # Copyright 1996, 1997, 1998, 2000, 2001, 2002, 2005, 2006 # by Russ Allbery <rra@stanford.edu> and Zenin # # This program is free software; you may redistribute it and/or modify + it # under the same terms as Perl itself. # # Ah, September, when the sysadmins turn colors and fall off the trees +.... # -- Dave Van Domelen ###################################################################### +######## # Modules and declarations ###################################################################### +######## package Term::ANSIColor; require 5.001; use strict; use vars qw($AUTOLOAD $AUTORESET $EACHLINE @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION %attributes %attributes_r); use Exporter (); @ISA = qw(Exporter); @EXPORT = qw(color colored); @EXPORT_OK = qw(uncolor); %EXPORT_TAGS = (constants => [qw(CLEAR RESET BOLD DARK UNDERLINE UNDER +SCORE BLINK REVERSE CONCEALED BLACK RED GRE +EN YELLOW BLUE MAGENTA CYAN WHITE ON_BLA +CK ON_RED ON_GREEN ON_YELLOW ON_BLUE ON_ +MAGENTA ON_CYAN ON_WHITE)]); Exporter::export_ok_tags ('constants'); $VERSION = '1.11'; ###################################################################### +######## # Internal data structures ###################################################################### +######## %attributes = ('clear' => 0, 'reset' => 0, 'bold' => 1, 'dark' => 2, 'underline' => 4, 'underscore' => 4, 'blink' => 5, 'reverse' => 7, 'concealed' => 8, 'black' => 30, 'on_black' => 40, 'red' => 31, 'on_red' => 41, 'green' => 32, 'on_green' => 42, 'yellow' => 33, 'on_yellow' => 43, 'blue' => 34, 'on_blue' => 44, 'magenta' => 35, 'on_magenta' => 45, 'cyan' => 36, 'on_cyan' => 46, 'white' => 37, 'on_white' => 47); # Reverse lookup. Alphabetically first name for a sequence is preferr +ed. for (reverse sort keys %attributes) { $attributes_r{$attributes{$_}} = $_; } ###################################################################### +######## # Implementation (constant form) ###################################################################### +######## # Time to have fun! We now want to define the constant subs, which ar +e named # the same as the attributes above but in all caps. Each constant sub + needs # to act differently depending on whether $AUTORESET is set. Without # autoreset: # # BLUE "text\n" ==> "\e[34mtext\n" # # If $AUTORESET is set, we should instead get: # # BLUE "text\n" ==> "\e[34mtext\n\e[0m" # # The sub also needs to handle the case where it has no arguments corr +ectly. # Maintaining all of this as separate subs would be a major nightmare, + as well # as duplicate the %attributes hash, so instead we define an AUTOLOAD +sub to # define the constant subs on demand. To do that, we check the name o +f the # called sub against the list of attributes, and if it's an all-caps v +ersion # of one of them, we define the sub on the fly and then run it. # # If the environment variable ANSI_COLORS_DISABLED is set, turn all of + the # generated subs into pass-through functions that don't add any escape # sequences. This is to make it easier to write scripts that also wor +k on # systems without any ANSI support, like Windows consoles. sub AUTOLOAD { my $enable_colors = !defined $ENV{ANSI_COLORS_DISABLED}; my $sub; ($sub = $AUTOLOAD) =~ s/^.*:://; my $attr = $attributes{lc $sub}; if ($sub =~ /^[A-Z_]+$/ && defined $attr) { $attr = $enable_colors ? "\e[" . $attr . 'm' : ''; eval qq { sub $AUTOLOAD { if (\$AUTORESET && \@_) { '$attr' . "\@_" . "\e[0m"; } else { ('$attr' . "\@_"); } } }; goto &$AUTOLOAD; } else { require Carp; Carp::croak ("undefined subroutine &$AUTOLOAD called"); } } ###################################################################### +######## # Implementation (attribute string form) ###################################################################### +######## # Return the escape code for a given set of color attributes. sub color { return '' if defined $ENV{ANSI_COLORS_DISABLED}; my @codes = map { split } @_; my $attribute = ''; foreach (@codes) { $_ = lc $_; unless (defined $attributes{$_}) { require Carp; Carp::croak ("Invalid attribute name $_"); } $attribute .= $attributes{$_} . ';'; } chop $attribute; ($attribute ne '') ? "\e[${attribute}m" : undef; } # Return a list of named color attributes for a given set of escape co +des. # Escape sequences can be given with or without enclosing "\e[" and "m +". The # empty escape sequence '' or "\e[m" gives an empty list of attrs. sub uncolor { my (@nums, @result); for (@_) { my $escape = $_; $escape =~ s/^\e\[//; $escape =~ s/m$//; unless ($escape =~ /^((?:\d+;)*\d*)$/) { require Carp; Carp::croak ("Bad escape sequence $_"); } push (@nums, split (/;/, $1)); } for (@nums) { $_ += 0; # Strip leading zeroes my $name = $attributes_r{$_}; if (!defined $name) { require Carp; Carp::croak ("No name for escape sequence $_" ); } push (@result, $name); } @result; } # Given a string and a set of attributes, returns the string surrounde +d by # escape codes to set those attributes and then clear them at the end +of the # string. The attributes can be given either as an array ref as the f +irst # argument or as a list as the second and subsequent arguments. If $E +ACHLINE # is set, insert a reset before each occurrence of the string $EACHLIN +E and # the starting attribute code after the string $EACHLINE, so that no a +ttribute # crosses line delimiters (this is often desirable if the output is to + be # piped to a pager or some other program). sub colored { my ($string, @codes); if (ref $_[0]) { @codes = @{+shift}; $string = join ('', @_); } else { $string = shift; @codes = @_; } return $string if defined $ENV{ANSI_COLORS_DISABLED}; if (defined $EACHLINE) { my $attr = color (@codes); join '', map { $_ ne $EACHLINE ? $attr . $_ . "\e[0m" : $_ } grep { length ($_) > 0 } split (/(\Q$EACHLINE\E)/, $string); } else { color (@codes) . $string . "\e[0m"; } } package main; die "YOUR CODE HERE";
    If that doesn't help you, you need to learn " ANSI escape sequences".
      Anonymous Monk, thank you for your reply. Perhaps I should have said I couldn’t use modules because I don’t know how to download and compile anything in MachTen. I had previously tried to upgrade to Perl 5.8 but couldn’t. I’m just a user. Other than the program I have which I tried to enhance, I don’t program or do any kind of system administration. I am seventy two and retired. I have bought several Perl books and tried to learn Perl programing, but because I don’t program for a living, or get enough Perl practice, I forget what I learned after a while. I no longer have the retention I used to have. I will try again to see if I can install the latest stable Perl in MachTen. One day, I will buy a new Mac, and hopefully I’ll have what I need. I regret any inconvenience I have caused you or the other members. I didn’t know who else to ask.
        I was successful in building perl-5.6.2 on Machten 4.1.4. That is the highest version that is supported. That’s all it took for me to be able to underline because it contains the module Term::ANSIColor. Now all I have to do is to get it to work on my file. Thanks for your help.