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

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

I have this line is an sql file.
GRANT SELECT, INSERT, DELETE, UPDATE ON "ARSAMS"."LOCATION_REPAIR_PART +S_MASTER" TO "ARSAMS" / / /
I cannot seem to figure out how to remove the extra two / without removing the first /. If I do s/\///g; it removes all / from the file. This is not what I want. I just need to remove the last two / after this statement. Does anyone have any advice.

Thanks
Bobby

Title edit by tye

Replies are listed 'Best First'.
Re: What would be the best way?
by BrowserUk (Pope) on Oct 15, 2002 at 12:21 UTC

    Assuming that 'line' is in $string...

    $string =~ s!\n/\n/!!

    Cor! Like yer ring! ... HALO dammit! ... 'Ave it yer way! Hal-lo, Mister la-de-da. ... Like yer ring!
Re: What would be the best way?
by Abigail-II (Bishop) on Oct 15, 2002 at 13:37 UTC
    The following program reads in a file, and if it finds a line that starts with GRANT, followed by a line containing whatever, followed by two lines containing just a '/', it will remove the slashes from the latter two lines. This is how I interpret what you want; however, your specification isn't very exact.
    use strict; use warnings; my @buffer; for (1 .. 4) { push @buffer => scalar <>; last unless defined $buffer [-1]; } while (defined $buffer [-1]) { if ($buffer [0] =~ /^GRANT/ && $buffer [2] eq "/\n" && $buffer [3] eq "/\n") { $buffer [2] = $buffer [3] = "\n"; } print shift @buffer; push @buffer => scalar <>; } pop @buffer; print @buffer; __END__

    Abigail

(tye)Re: How to make only two of three substitutions?
by tye (Sage) on Oct 15, 2002 at 13:23 UTC

        $str =~ s#/##   for  1,2;
    or     s#(/\s*){2}\z##
    for example. But these assume you slurp the entire file into a string. Perhaps you are doing a perl -pe kind of thing...     perl -i.old -pe '$sawSlash++ && s#/## if m#/#'
    (updated again. Thanks, blakem. I added "$str =~" to match what I was thinking and what I had tested.)

            - tye (saaa x y z aziez,s $e $s deedeydi,t $ex,print)
      s#/## for 1,2;
      That won't work. $_ gets aliased in the for loop to the constants '1' and '2'. You could get away with:
      for my $i (1,2) { s#/## }
      Though, its not real pretty...

      -Blake

Re: How to make only two of three substitutions?
by tommyw (Hermit) on Oct 15, 2002 at 13:30 UTC

    Oh well, if everybody's joining in:

    s|/{2,}|/|;
    which will remove an unlimited number of extra slashes.

    And if you've got multiline strings: s|(?:/\n){2,}|/\n|;

    --
    Tommy
    Too stupid to live.
    Too stubborn to die.

Re: How to make only two of three substitutions?
by bigj (Monk) on Oct 15, 2002 at 15:18 UTC
    Another way, expressing the logic, that the first / mustn't removed, but all others is to say it explizitly.
    Assuming the sql statement is one line $sql:
    1 while $sql =~ s%(/.*)/%$1%gs;
    or with the futuring keep syntax:
    use Regexp::Keep; # I believe, it will be unnecessary on Perl 5.10 1 while $sql =~ s%/.*\K/%%gs;

    Greetings,
    Janek

Re: What would be the best way?
by hotshot (Prior) on Oct 15, 2002 at 12:55 UTC
    you can always read the next line after your long line by:
    $line = <FILE>; ### this will give you the first / that you want to +preserve
    and remove any / after it using the regexp you mentioned

    Hotshot
Re: How to make only two of three substitutions?
by nothingmuch (Priest) on Oct 15, 2002 at 21:44 UTC
    Something which I believe may be of some interest to you is the \G zero width assertion.
    $_ = "1/ 2/ 3/"; m{/}g; # will set pos to 2, the offset of the first '/' s{\G(.*?)/}{$1}g; # \G is like ^, but instead of matching # a line begining it matches the place # where the last /g modified regexp left # off. print;
    The above code first finds a slash, and saves it's position in the lvalue acccessible by pos($_). Because the m//g is not performed in list context, the slashes are not all pushed to a list and returned. Scalar/void context m//g lets you step through the string, in an iterational fashion.
    Next, the substitution is performed so that the match has to succeed \G. \G is the place where the last /g left off. /g modification on s/// substitutes everything regardless of context. The first slash found, and it's position noted, we proceed to find a minimal anything, followed by a slash. Once we find that, we replace it with the minimal anything, and remember the position. We then search onward for more slashes, until no match can be made.

    This specific example is a bit flawed, because it invloves a lot of copying, in theory, and it is also not the best choice for your specific example, but I just thought it may be useful to know.
    perlre contains more info.

    -nuffin
    zz zZ Z Z #!perl