[comp.lang.perl] Need detab function

stu@gtisqr.uucp (Stu Donaldson) (11/20/90)

I need a detab function written in perl.  I'm sure there must be
something other than the brute force method of implimenting this.

Any help would be appreciated...

	How about a one liner? :-)

-- Stu --
Stu Donaldson          UUCP: {smart-host}!gtisqr!stu
Maverick Microsystems  ARPA: gtisqr!stu@beaver.cs.washington.edu
Mukilteo, Washington   Bell: (206) 743-6659

merlyn@iwarp.intel.com (Randal Schwartz) (11/21/90)

In article <1990Nov19.210123.20558@gtisqr.uucp>, stu@gtisqr (Stu Donaldson) writes:
| I need a detab function written in perl.  I'm sure there must be
| something other than the brute force method of implimenting this.
| 
| Any help would be appreciated...
| 
| 	How about a one liner? :-)

Like maybe:

	perl -pe '1 while s/\t/" " x (8-length($`)%8)/e'

This is not the same as:

	perl -pe 's/\t/" " x (8-length($`)%8)/eg' # wrong!

Although I didn't test it, I recall something about length($`) being
wrong in a global edit, so I skipped that version.  The first one
works, however.

print "Just another Perl hacker,"
-- 
/=Randal L. Schwartz, Stonehenge Consulting Services (503)777-0095 ==========\
| on contract to Intel's iWarp project, Beaverton, Oregon, USA, Sol III      |
| merlyn@iwarp.intel.com ...!any-MX-mailer-like-uunet!iwarp.intel.com!merlyn |
\=Cute Quote: "Intel: putting the 'backward' in 'backward compatible'..."====/

lwall@jpl-devvax.JPL.NASA.GOV (Larry Wall) (11/21/90)

In article <1990Nov19.210123.20558@gtisqr.uucp> stu@gtisqr.uucp (Stu Donaldson) writes:
: I need a detab function written in perl.  I'm sure there must be
: something other than the brute force method of implimenting this.
: 
: Any help would be appreciated...
: 
: 	How about a one liner? :-)

Depends on what you mean by "detab".  If you mean get rid of leading tabs:

	s/^(\t+)/' ' x (length($1) * 8)/e;

If you mean get rid of all tabs like expand(1) does, then do this:

	0 while s/\t/' ' x (8 - length($`) % 8)/e;

If you mean to reduce the indentation of your lines, here's a program
called retab:

#!/usr/bin/perl
'di';
'ig00';
#
# $Header: retab,v 1.1 90/08/12 00:21:20 lwall Locked $
#
# $Log:	retab,v $
# Revision 1.1  90/08/12  00:21:20  lwall
# Initial revision
# 

$sw = 4;
$sw = $1, shift if $ARGV[0] =~ /^-(\d+)/;

while (<>) {
    s#^( +)#"\t" x (length($1) / 8) . " " x (length($1) % 8)#e;
    s#^(\t*)( *)#' ' x ($sw * length($1)) . ' ' x (length($2) * $sw / 8)#e;
    s#^( +)#"\t" x (length($1) / 8) . " " x (length($1) % 8)#e;
    print;
}

##############################################################################

	# These next few lines are legal in both Perl and nroff.

.00;			# finish .ig
 
'di			\" finish diversion--previous line must be blank
.nr nl 0-1		\" fake up transition to first page again
.nr % 0			\" start at page 1
'; __END__ ############# From here on it's a standard manual page ############
.TH RETAB 1 "August 11, 1990"
.AT 3
.SH NAME
retab \- change indentation from every 8 columns to fewer columns
.SH SYNOPSIS
.B retab [-<tabwidth>] [files]
.SH DESCRIPTION
.I Retab
reads its input and shrinks all the initial whitespace down by some
factor.
By default it changes 8 spaces (one hardware tab) to 4 spaces, but
you can give a switch to change it to any number of spaces.
.PP
Processing happens in three stages on each line.
First all initial tabs are converted to the equivalent number of spaces.
Then the number of spaces are reduced by the specified amount.
Then leading spaces are converted back to tabs 8 spaces at a time.
.SH ENVIRONMENT
No environment variables are used.
.SH FILES
None.
.SH AUTHOR
Larry Wall
.SH BUGS
It might not pick exactly the same spacing you might pick for half-indented
lines when converting from, say, 8 to 3 columns per indent.

Larry
(No, I'm not one of the bugs...)

niklas@appli.se (Niklas Hallqvist) (11/21/90)

stu@gtisqr.uucp (Stu Donaldson) writes:

>I need a detab function written in perl.  I'm sure there must be
>something other than the brute force method of implimenting this.

>Any help would be appreciated...

>	How about a one liner? :-)

Well, Here's my try:

perl -pe 's/^([^\t]*)\t/$1." "x(8-length($1)%8)/e while /\t/;'

Now, this is a bad one, since I loop once for each tab on a line.
I thought this one would take care of that problem, but NO, it did NOT!

perl -pe 's/^([^\t]*)\t/$1." "x(8-length($1)%8)/eg;'

Shouldn't the g flag work with the e flag, doing just what I did explicitly
in my first try?

Now, don't worry... I'm sure Randal & Larry will replace this script
with a thirty-byter... ;^)

						Niklas

-- 
Niklas Hallqvist	Phone: +46-(0)31-40 75 00
Applitron Datasystem	Fax:   +46-(0)31-83 39 50
Molndalsvagen 95	Email: niklas@appli.se
S-412 63  GOTEBORG, Sweden     mcsun!sunic!chalmers!appli!niklas

utashiro@sran84.sra.co.jp (Kazumasa Utashiro) (11/21/90)

In article <1990Nov20.175000.1407@iwarp.intel.com>
	merlyn@iwarp.intel.com (Randal Schwartz) writes:
>> | I need a detab function written in perl.  I'm sure there must be
>> | something other than the brute force method of implimenting this.
>> 
>> Like maybe:
>> 
>> 	perl -pe '1 while s/\t/" " x (8-length($`)%8)/e'

To handle control character correctly, I'm doing like this:

	1 while s/\t/" " x (8-&width($`)%8)/e;
	sub width {
	    local($_)=shift;
	    return(length($_)) unless (/[\033\010\f\r]/);
	    s/^.*[\f\r]//;
	    s/\033\$[\@B]|\033\([JB]//g; # this is for Japanese code
	    1 while s/[^\010]\010//;
	    s/^\010*//;
	    length($_);
	}

Is there any better way?

---
K. Utashiro
utashiro@sra.co.jp

inc@tc.fluke.COM (Gary Benson) (11/21/90)

In article <1990Nov19.210123.20558@gtisqr.uucp> stu@gtisqr.uucp (Stu Donaldson) writes:
>I need a detab function written in perl.  I'm sure there must be
>something other than the brute force method of implimenting this.
>
>Any help would be appreciated...
>
>	How about a one liner? :-)
>

Well it's not a one-liner, and I didn't even write it -- credit Craig
Johnson here at Fluke for it:

	while (/\t/) {
	  $x = index($_, "\t");
	  $n = 8 - ($x & 7);
	  $spaces = (" " x $n);
	  s/\t/$spaces/;

This relies on the unix 8-spaces-per-tab convention. Depending on your exact
circumstances, you may need to change the expression in the assignment of $n.
For a while, I thought that our emacs had been set up to create and expect
5-space tabs, so Craig changed the third line for me to read:

    $n = 5 - ($x % 5); 

-- 
Gary Benson    -=[ S M I L E R ]=-   -_-_-_-inc@fluke.com_-_-_-_-_-_-_-_-_-_-

If we make peaceful revolution impossible, we make violent revolution
inevitable.   -John F. Kennedy