ANSI colors and the space they take.

S

Sandman

I like ansi colors, but I've run in to this problem:

printf "\e[32;01m%-14s\e[00;00m \e[37;01m%-30s\e[00;00m\n", "Hello", "World!";
printf "%-14s %-30s\n", "Hello", "World!";
printf "%-14s %-30s\n", "\e[32;01mHello\e[00;00m", "\e[37;01mWorld!\e[00;00m";

This results in:

Hello World!
Hello World!
Hello World!

(albeit color-coded)

In the last example, the length of the actual ansi code seems to be taken into
consideration for printf - is there a way to elaviate this?
 
J

James Willmore

I like ansi colors, but I've run in to this problem:

printf "\e[32;01m%-14s\e[00;00m \e[37;01m%-30s\e[00;00m\n", "Hello",
"World!"; printf "%-14s %-30s\n", "Hello", "World!";
printf "%-14s %-30s\n", "\e[32;01mHello\e[00;00m",
"\e[37;01mWorld!\e[00;00m";

This results in:

Hello World!
Hello World!
Hello World!

(albeit color-coded)

In the last example, the length of the actual ansi code seems to be
taken into consideration for printf - is there a way to elaviate
this?

You could save yourself the headaches and use the Term::ANSIColor
module :) It *may* be what you want.

HTH

--
Jim

Copyright notice: all code written by the author in this post is
released under the GPL. http://www.gnu.org/licenses/gpl.txt
for more information.

a fortune quote ...
Time is nature's way of making sure that everything doesn't
happen at once.
 
M

Michele Dondi

I like ansi colors, but I've run in to this problem: [snip]
In the last example, the length of the actual ansi code seems to be taken into
consideration for printf - is there a way to elaviate this?

perldoc -q color
perldoc Term::ANSIColor


Michele
 
S

Sandman

Michele Dondi said:
I like ansi colors, but I've run in to this problem:
[snip] In the last example, the length of the actual ansi code seems
to be taken into consideration for printf - is there a way to elaviate
this?

perldoc -q color
perldoc Term::ANSIColor

Thanks, but that didn't help much:

#!/usr/bin/perl

use Term::ANSIColor;
printf "%-14s %-30s\n", "Hello", "World!";
printf "%-14s %-30s\n", "\e[32;01mHello\e[00;00m", "\e[37;01mWorld!\e[00;00m";
printf "%-14s %-30s\n", color("red") . "Hello" . color("reset"), color("green") . "World!" . color("reset");
printf "%-14s %-30s\n", colored("Hello", "red"), colored("World!", "green");

use Term::ANSIColor qw:)constants);
printf "%-14s %-30s\n", RED . "Hello" . RESET, GREEN . "World!" . RESET;

<output>
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
</output>

Seems to me that color(), colored() or the constants eat up as much space as the escape codes.
 
J

Jay Tilton

: In article <[email protected]>,
:
: >
: >> I like ansi colors, but I've run in to this problem:
: > [snip] In the last example, the length of the actual ansi code seems
: > to be taken into consideration for printf - is there a way to elaviate
: > this?
: >
: > perldoc -q color
: > perldoc Term::ANSIColor
:
: Thanks, but that didn't help much:
:
: #!/usr/bin/perl
:
: use Term::ANSIColor;
: printf "%-14s %-30s\n", "Hello", "World!";
: printf "%-14s %-30s\n", "\e[32;01mHello\e[00;00m", "\e[37;01mWorld!\e[00;00m";
: printf "%-14s %-30s\n", color("red") . "Hello" . color("reset"), color("green") . "World!" . color("reset");
: printf "%-14s %-30s\n", colored("Hello", "red"), colored("World!", "green");
:
: use Term::ANSIColor qw:)constants);
: printf "%-14s %-30s\n", RED . "Hello" . RESET, GREEN . "World!" . RESET;
:
: <output>
: Hello World!
: Hello World!
: Hello World!
: Hello World!
: Hello World!
: </output>
:
: Seems to me that color(), colored() or the constants eat up as much
: space as the escape codes.

That's true, but you can put the ANSI escape sequences into printf's
formatting string, much as you did in the first example in your original
article:

printf "\e[32;01m%-14s\e[00;00m \e[37;01m%-30s\e[00;00m\n",
"Hello", "World!";

Using the Term::ANSIColor subroutines, that might go like:

use Term::ANSIColor;
my $fmt = colored('%-14s', 'bold green on_black') . ' '
. colored('%-30s', 'bold white on_black') . "\n";
printf $fmt, "Hello", "World!";
 
J

James Willmore

<output>
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
</output>

Seems to me that color(), colored() or the constants eat up as much
space as the escape codes.

Maybe you're looking for something like this (?) .............

----------------------------------------------------------------
#!/usr/bin/perl

use Term::ANSIColor qw:)constants);

my $hello = sprintf "%14s", RED. "Hello".RESET;
my $world = sprintf "%30s", GREEN. "World".RESET;

print "$hello $world\n";
----------------------------------------------------------------

<output>
jim@maxine:~> perl news11.pl
Hello World
jim@maxine:~>
</output>

HTH

--
Jim

Copyright notice: all code written by the author in this post is
released under the GPL. http://www.gnu.org/licenses/gpl.txt
for more information.

a fortune quote ...
'I believe in getting into hot water; it keeps you clean." --
G. K. Chesterton
 
S

Sandman

[email protected] (Jay Tilton) said:
: Seems to me that color(), colored() or the constants eat up as much
: space as the escape codes.

That's true, but you can put the ANSI escape sequences into printf's
formatting string, much as you did in the first example in your original
article:

printf "\e[32;01m%-14s\e[00;00m \e[37;01m%-30s\e[00;00m\n",
"Hello", "World!";

Using the Term::ANSIColor subroutines, that might go like:

use Term::ANSIColor;
my $fmt = colored('%-14s', 'bold green on_black') . ' '
. colored('%-30s', 'bold white on_black') . "\n";
printf $fmt, "Hello", "World!";

I realize this, of course. BUt I would very much like to keep the formatting
seperate from the printf's. It may not be critical for the application I am
writing now, but I know some script I will try to "ansify" that will not accept
that method.
 
S

Sandman

<output>
Hello World!
Hello World!
Hello World!
Hello World!
Hello World!
</output>

Seems to me that color(), colored() or the constants eat up as much
space as the escape codes.

Maybe you're looking for something like this (?) .............

----------------------------------------------------------------
#!/usr/bin/perl

use Term::ANSIColor qw:)constants);

my $hello = sprintf "%14s", RED. "Hello".RESET;
my $world = sprintf "%30s", GREEN. "World".RESET;

print "$hello $world\n";
----------------------------------------------------------------

<output>
jim@maxine:~> perl news11.pl
Hello World
jim@maxine:~>
</output>[/QUOTE]

Well, thanks for the input. I can think of many ways to bypass the problematic
result, and it may have been unwise of me not to state from the beginning that
the color-formatting of the strings should be kept seperate from the print
formatting, as well as the printf structure needs to be kept in the printf,
especially in a particular application I've got.
 
M

Michele Dondi

That's true, but you can put the ANSI escape sequences into printf's
formatting string, much as you did in the first example in your original
article:
[snip]
I realize this, of course. BUt I would very much like to keep the formatting
seperate from the printf's. It may not be critical for the application I am
writing now, but I know some script I will try to "ansify" that will not accept
that method.

Maybe you're looking for something like this (?) .............
[snip]
Well, thanks for the input. I can think of many ways to bypass the problematic
result, and it may have been unwise of me not to state from the beginning that
the color-formatting of the strings should be kept seperate from the print
formatting, as well as the printf structure needs to be kept in the printf,
especially in a particular application I've got.

I'm still not sure I fully understand what your problem is. I
suggested, as others did, Term::ANSIColor because it will help you
writing cleaner code that IMHO will be easier to maintain too.

I am still under the impression that you may/should choose a simpler
logic rather than fiddling with this approach. However, if you really
want to "keep the printf() formatting and the color formatting"
separate, an idea that comes to mind is:

format your strings with sprintf(), then use s///ge to suitably
"highlight" some "words". For example,


#!/usr/bin/perl -l

use strict;
use warnings;
use Term::ANSIColor qw/:constants/;

$_=sprintf '%-14s %-30s', qw/Hello World/;
print;
my @colors=(GREEN, RED);
my $i=0;
s/\b\w+\b/$colors[$i++] . $& . RESET/ge;
print;

__END__


Of course this is a very naive example and things may get definitely
more complicated in a real application; I think it all depends on how
"regular" you strings are, though.


Michele
 
S

Sandman

Well, thanks for the input. I can think of many ways to bypass the
problematic
result, and it may have been unwise of me not to state from the beginning
that
the color-formatting of the strings should be kept seperate from the print
formatting, as well as the printf structure needs to be kept in the printf,
especially in a particular application I've got.

I'm still not sure I fully understand what your problem is. I
suggested, as others did, Term::ANSIColor because it will help you
writing cleaner code that IMHO will be easier to maintain too.[/QUOTE]


I appreciate the suggestions, but my problem is that I have an output routine,
which I use when I want to output something to STDOUT from my script(s). Now, I
wanteed to colorenable that output. This is a simplified version:

--
#!/usr/bin/perl

&output("Warning", "Hello World!");

sub output {
printf "%-14s %-30s\n", $_[0], $_[1];
}
--

Now, $_[0] is the "type" of output, so I'll be able to distinguish it from the
rest. Problem is, that I can't anticipate, globally, every kind of type. And
even so, I'm not sure I like to have the entire row in one color (i.e. a
"warning" => "red" hash and then fetch the colors from that, in the sub).

So, what I wanted to do was this (now using Term::Ansi):

--
#!/usr/bin/perl
use Term::ANSIColor;
&output(colored("Warning", "red"), colored("Hello World!", "green"));

sub output {
printf "%-14s %-30s\n", $_[0], $_[1];
}
--

My problem here is that the above results in a messy printf. In short, the
colorcoding should be passed on to the sub routine without adding arguments to
it (i.e. not &output(RED, "Warning", GREEN, "Hello world");) since I'm using it
in so many places that I don't want to change. This is also why I'll probably
colorencode some "well known" messages types, so even output from older scripts
will have color.

There is one thing I can do, and that's to check if the incoming arguments have
colorcoding, allthough I'm unsure on how to do that (=~m/e[/ doesn't yield a
match, regardless if I type the ansi code or use Term::Ansi) and alter the
value of "%-14s", but that strikes me as a poor solution.
 
J

Jay Tilton

: My problem here is that the above results in a messy printf. In short, the
: colorcoding should be passed on to the sub routine without adding arguments to
: it (i.e. not &output(RED, "Warning", GREEN, "Hello world");) since I'm using it
: in so many places that I don't want to change.

You don't have to change them. You could pass colorizing information in
an optional argument that doesn't make the sub incompatible with the
existing calls. Like, say:

output("Warning", "Hello world!");
output("Warning", "Hello world!", ['red', 'green']);
sub output {
my $fmt = "%-14s %-30s\n";
if( ref($_[-1]) eq 'ARRAY' ) {
$fmt = colored('%-14s', $_[-1][0]) . ' '
. colored('%-30s', $_[-1][1]) . "\n",
}
printf $fmt, @_[0,1];
}

: There is one thing I can do, and that's to check if the incoming arguments have
: colorcoding, allthough I'm unsure on how to do that (=~m/e[/ doesn't yield a
: match, regardless if I type the ansi code or use Term::Ansi)

"e" is not "\e" . Try "m/\e\[/" intead.

: and alter the
: value of "%-14s", but that strikes me as a poor solution.

Agreed. You're looking at a ground-up rewrite of printf() that can
adequately parse and alter the field widths in a formatting string to
accommodate character sequences that render with zero width. Blecch!

An alternative that has not been considered yet is to cut printf() out
of the picture completely and use ANSI cursor positioning sequences
instead.

use Term::ANSIScreen qw:)color :cursor);
output2( "Warning", "Hello World!" );
output2(
colored("Warning", "red"),
colored("Hello World!", "green")
);
sub output2 {
print savepos(), $_[0], loadpos(), right(15), $_[1], "\n";
}
 
S

Sandman

[email protected] (Jay Tilton) said:
: My problem here is that the above results in a messy printf. In short, the
: colorcoding should be passed on to the sub routine without adding arguments
: to
: it (i.e. not &output(RED, "Warning", GREEN, "Hello world");) since I'm
: using it
: in so many places that I don't want to change.

You don't have to change them. You could pass colorizing information in
an optional argument that doesn't make the sub incompatible with the
existing calls. Like, say:

output("Warning", "Hello world!");
output("Warning", "Hello world!", ['red', 'green']);
sub output {
my $fmt = "%-14s %-30s\n";
if( ref($_[-1]) eq 'ARRAY' ) {
$fmt = colored('%-14s', $_[-1][0]) . ' '
. colored('%-30s', $_[-1][1]) . "\n",
}
printf $fmt, @_[0,1];
}

: There is one thing I can do, and that's to check if the incoming arguments
: have
: colorcoding, allthough I'm unsure on how to do that (=~m/e[/ doesn't yield
: a
: match, regardless if I type the ansi code or use Term::Ansi)

"e" is not "\e" . Try "m/\e\[/" intead.

: and alter the
: value of "%-14s", but that strikes me as a poor solution.

Agreed. You're looking at a ground-up rewrite of printf() that can
adequately parse and alter the field widths in a formatting string to
accommodate character sequences that render with zero width. Blecch!

An alternative that has not been considered yet is to cut printf() out
of the picture completely and use ANSI cursor positioning sequences
instead.

use Term::ANSIScreen qw:)color :cursor);
output2( "Warning", "Hello World!" );
output2(
colored("Warning", "red"),
colored("Hello World!", "green")
);
sub output2 {
print savepos(), $_[0], loadpos(), right(15), $_[1], "\n";
}

Your post gave me some ideas, I'll play around with them and see if I can come
up with a neat solution. Thanks alot!
 
S

Sandman

I appreciate the suggestions, but my problem is that I have an output
routine, which I use when I want to output something to STDOUT from
my script(s). Now, I wanteed to colorenable that output. This is a
simplified version:

#!/usr/bin/perl

&output("Warning", "Hello World!");

As a side note, you should never ever call a sub with "&" unless you
know *why* you're doing it. IIRC you've already been warned about
this...[/QUOTE]

Oh, ok. Sorry about that. I don't remember that warning, however.
You'd better adopt some sort of "homemade markup" whose *reliability*
will depend on the complexity of the strings you need to output.
Please see if the following script can give you an idea on how to
proceed...

#!/usr/bin/perl -l

use strict;
use warnings;
use Term::ANSIColor qw/:constants/;

sub output;

{
my %col=( 'W' => RED,
'E' => GREEN );

sub output {
die "Wrong number of args to output()"
unless @_ == 2;
$_=sprintf '%-14s %-30s', @_;
no warnings 'uninitialized';
s/(\w)<(.*?)>/$col{$1} . $2 . RESET/ge;
print;
}
}

output 'warning', 'Hello World!';
output 'W<warning>', 'Hello World!';
output 'W<warning>', 'E<Hello World!>';
output 'X<warning>', 'E<Hello World!>';

__END__

As you can see any unrecognized "tag" will silently evaporate and can
thus be used to avoid screwing up the formatting. Since *any* tag of
the kind recognized by s/// above takes three chars you may need to
modify the sprintf() format accordingly, though...

This was the best suggestion so far. I did it this way:

#!/usr/bin/perl
use Term::ANSIColor;
use strict;
use warnings;

sub output {
my %colors = (
"r", "red",
"b", "blue",
"g", "green",
"y", "yellow"
);
my @l = split "::", $_[0];
my @r = split "::", $_[1];
print $l[1] && $colors{$l[1]} ? sprintf(colored("%-14s ", $colors{$l[1]}), $l[0]) : sprintf("%-14s ", $l[0]);
print $r[1] && $colors{$r[1]} ? sprintf(colored("%-30s\n", $colors{$r[1]}), $r[0]) : sprintf("%-30s\n", $r[0]);
}

output("Warning::r", "Something happened::g");
output("Warning", "Something happened::g");
output("Warning::r", "Something happened");


That outputs everything correctly. Thanks!
 
M

Michele Dondi

I appreciate the suggestions, but my problem is that I have an output routine,
which I use when I want to output something to STDOUT from my script(s). Now, I
wanteed to colorenable that output. This is a simplified version:

#!/usr/bin/perl

&output("Warning", "Hello World!");

As a side note, you should never ever call a sub with "&" unless you
know *why* you're doing it. IIRC you've already been warned about
this...
sub output {
printf "%-14s %-30s\n", $_[0], $_[1];
}

Now, $_[0] is the "type" of output, so I'll be able to distinguish it from the
rest. Problem is, that I can't anticipate, globally, every kind of type. And

Then you could plainly choose a suitable default for any "kind of
type" you didn't anticipate.
even so, I'm not sure I like to have the entire row in one color (i.e. a
"warning" => "red" hash and then fetch the colors from that, in the sub).

But this is not a problem, see for example the following script (yet
another oversimplified exaple!):

#!/usr/bin/perl -l

use strict;
use warnings;
use Term::ANSIColor qw/:constants/;

{
my %what=( 'warning' => [ RED, GREEN ],
'whatever' => [ BLUE, BLUE ] );
sub output {
die "Wrong number of args to output()"
unless @_ == 2;
$_=sprintf '%-14s %-30s', @_;
print;
for my $i (0,1) {
no warnings 'uninitialized';
my $tmp=$_[$i];
s/\b\Q$_[$i]\E\b/$what{$_[0]}->[$i] . $& . RESET/e;
}
print;
}
}

output('warning', 'Hello World');

__END__

So, what I wanted to do was this (now using Term::Ansi):

#!/usr/bin/perl
use Term::ANSIColor;
&output(colored("Warning", "red"), colored("Hello World!", "green"));

sub output {
printf "%-14s %-30s\n", $_[0], $_[1];
}

My problem here is that the above results in a messy printf. In short, the
colorcoding should be passed on to the sub routine without adding arguments to
it (i.e. not &output(RED, "Warning", GREEN, "Hello world");) since I'm using it
in so many places that I don't want to change. This is also why I'll probably
colorencode some "well known" messages types, so even output from older scripts
will have color.

As I said in my other post, your whole strategy seems rather awkward
to me, and knowing the further details you gave in this post
strengthened this impression of mine. But I'm not here to judge it...
There is one thing I can do, and that's to check if the incoming arguments have
colorcoding, allthough I'm unsure on how to do that (=~m/e[/ doesn't yield a
match, regardless if I type the ansi code or use Term::Ansi) and alter the
value of "%-14s", but that strikes me as a poor solution.

In any case messing with ANSI sequences, printf() and s/// indeed
seems a poor solution.

You'd better adopt some sort of "homemade markup" whose *reliability*
will depend on the complexity of the strings you need to output.
Please see if the following script can give you an idea on how to
proceed...

#!/usr/bin/perl -l

use strict;
use warnings;
use Term::ANSIColor qw/:constants/;

sub output;

{
my %col=( 'W' => RED,
'E' => GREEN );

sub output {
die "Wrong number of args to output()"
unless @_ == 2;
$_=sprintf '%-14s %-30s', @_;
no warnings 'uninitialized';
s/(\w)<(.*?)>/$col{$1} . $2 . RESET/ge;
print;
}
}

output 'warning', 'Hello World!';
output 'W<warning>', 'Hello World!';
output 'W<warning>', 'E<Hello World!>';
output 'X<warning>', 'E<Hello World!>';

__END__

As you can see any unrecognized "tag" will silently evaporate and can
thus be used to avoid screwing up the formatting. Since *any* tag of
the kind recognized by s/// above takes three chars you may need to
modify the sprintf() format accordingly, though...


HTH,
Michele
 
M

Michele Dondi

Oh, ok. Sorry about that. I don't remember that warning, however.

This is why I wrote "IIRC". To be fair, I quicky checked, and it
really seems I did *not* RC! In any case giving a peek into 'perldoc
perlsub' may help...
You'd better adopt some sort of "homemade markup" whose *reliability*
[snip]
This was the best suggestion so far. I did it this way:

Thanks!


Michele
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top