http://www.perlmonks.org?node_id=203941

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

Does anyobody has an idea on how to replace tabs with spaces taking into consideration the position. If I just replace the tabs with a fixed amount of spaces I will get a wrong positioning. See my bad sample below. I would like to replace the tabs taking into consideration the total length between each word. Anybody has an idea.

Shape:r59 X-loc:9.77 Y-loc:5.608 Shape:r59 X-loc:9.8819 Y-loc:0

I had something like this but this would replace the tabs with a fixed amount of spaced resulting in

Shape:r59 X-loc:9.77 Y-loc:5.608 Shape:r59 X-loc:9.8819 Y-loc:0
for ($test) { s/\t/ /g; s/\s\*/\*/g; s/^([^\t]*)/sprintf('%-10s', $1)/e; }

Replies are listed 'Best First'.
Re: How to replace Tab with spaces not altering postion
by particle (Vicar) on Oct 09, 2002 at 15:11 UTC
    i suggest you look at the Text::Tabs module. from the docs:

    Text::Tabs -- expand and unexpand tabs per the unix expand(1) and unexpand(1)

    this will probably do what you want. if you're lucky enough to be using perl 5.8.0, it's in the core. otherwise, you'll have to download from the CPAN.

    ~Particle *accelerates*

      Text::Tabs will do the same as my example, it will replace the tabs with a fixed amount of spaces but it will not consider the spaces in between the two strings. If you make a tab it can contain one time 2 spaces, another time it contains 5 spaces, .... so if you replace the tabs with a fixed amount of spaces the positioning will be scrambled. No collumn effect anymore. So to make it work you first would have to check the amount of spaces between the two strings and then replace the tab with the amount of spaces it was representing. Sorry the Text::Tabs is working fine. It was a proportional font that was all the time giving the bad result and giving the impression that the spaces were incorrect. It is sometimes in a detail.

        Text::Tabs will do the same as my example, it will replace the tabs with a fixed amount of spaces but it will not consider the spaces in between the two strings.

        i disagree. from the description in the Text::Tabs pod:

        Text::Tabs does about what the unix utilities expand(1) and unexpand(1) do. Given a line with tabs in it, expand will replace the tabs with the appropriate number of spaces. Given a line with or without tabs in it, unexpand will add tabs when it can save bytes by doing so. Invisible compression with plain ascii!

        here's an example:

        #!/usr/bin/perl require 5.006_001; use strict; use warnings; $|++; use Text::Tabs; my $tabstop = 8; my $text = "12345678901234567890\n\thi\tbye"; print $text,$/,length $text,$/; my $newtext = expand($text) print $newtext, $/,length $newtext,$/; __END__ ## prints 12345678901234567890 hi bye 28 12345678901234567890 hi bye 40

        ...unless i'm missing something

        ~Particle *accelerates*

Re: How to replace Tab with spaces not altering postion
by BrowserUk (Patriarch) on Oct 09, 2002 at 16:27 UTC

    No modules and quicker than a regex, faster than a speeding bullet, makes you popular with the girls....:)

    #! perl -sw use strict; my $tabn = 8; my $tabc = '.'; #! Use a space, '.' is just for demonstration. while( <DATA> ) { print; my $p; substr($_,$p,1) = $tabc x ($tabn - $p % $tabn) while ($p=in +dex($_, "\t"))>-1; print; } __DATA__ the quick brown fox the quick brown fox the brown fox

    Gives

    c:\test>203941 the quick brown fox the quick brown fox the quick brown fox ........the.....quick...brown...fox the brown fox ................the.............brown...........fox c:\test>

    Note: The original data contained tabs....honest! I just couldn't think of an easy way to show them.

    Note 2: If it doesn't look like the output is correct, you are probably viewing it with a proportional font.


    Cor! Like yer ring! ... HALO dammit! ... 'Ave it yer way! Hal-lo, Mister la-de-da. ... Like yer ring!
Re: How to replace Tab with spaces not altering postion
by joe++ (Friar) on Oct 09, 2002 at 15:11 UTC
    Be creative with length() and the mod operator: '%'...
    Heck, there even exists a CPAN module Text::Tabs!

    --
    Cheers, Joe

Re: How to replace Tab with spaces not altering postion
by bart (Canon) on Oct 09, 2002 at 15:40 UTC
    (Reinventing wheels can be fun, if the solution isn't too obvious. Perhaps you shouldn't be using this for real work, but OTOH, it's a bit like solving a crossword puzzle.)

    I assume you want a tabstop per 8 characters? This appears to work, replacing each tab with a string of 1 to 8 spaces:

    while(s/\t/" " x (8 - $-[0]%8)/e) {}
    For example, in "a\tb", at the time of the replacement, $-[0] will be 1, so 7 spaces will be inserted, replacing the tab.

    Note that s///g won't do the right thing, because it doesn't take into account the length of what's on the left of the match after any previous replacements; instead it'll continue to use the old length, as if nothing had been replaced yet. Thus, for the second match in "\t\t", $-[0] will still be 1, the length of the first "\t", and not 8, for the string of spaces that (already) replaced it.

Re: How to replace Tab with spaces not altering postion
by petral (Curate) on Oct 10, 2002 at 15:11 UTC
    > perldoc -q tabs Found in /usr/local/lib/perl5/5.6.1/pod/perlfaq4.pod How do I expand tabs in a string? You can do it yourself: 1 while $string =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e; Or you can just use the Text::Tabs module (part of the standard +perl distribution). use Text::Tabs; @expanded_lines = expand(@lines_with_tabs); >
    and this from perlop:
    Occasionally, you can't use just a `/g' to get all the changes to occur that you might want. Here are two common cases: # put commas in the right places in an integer 1 while s/(\d)(\d\d\d)(?!\d)/$1,$2/g; # expand tabs to 8-column spacing 1 while s/\t+/' ' x (length($&)*8 - length($`)%8)/e;
    And this has been in the perl man page since perl1!
    print "\et" x ($tab/8), ' ' x ($tab%8); # tab over


      p

      The problem is it's using $` which incurs a huge performance penalty.

      Here's another OWTDI:

      $_ = join "", map { $_ . " " x (8 - length() % 8) } split /\t/, $_, -1; Update: blakem points out that this code is broken. It will pad all parts of the string to a multiple of 8, including the last one. Getting it not to is disappointingly awkward..
      $_ = join "", do { my @bits = split /\t/, $_, -1; (map { $_ . " " x (8 - length() % 8) } @bits[0..$#bits-1]), $bits[ +-1] };
      (Update is untested.)

      Makeshifts last the longest.

        join "", map{ $_ . (/\n/ ? "" : " " x (8 - length() & 7)) } split /\t/ ?

          p