in reply to Re: Re: Messing with a substring
in thread Messing with a substring

I'm pretty damn sure I don't understand the problem at all... But maybe, just maybe... Anyway, I'll give it a shot. How about:

my $str = "/One/Two/Three"; my @paths = ($str); push @paths, $str while($str =~ s{^(/.+)/[^/]*$}{$1}); print "$_\n" for @paths;

That prints:

/One/Two/Three /One/Two /One

Is that at all what you're looking for?

Update: Of course, that's destructive to $str so be sure it's a copy.

bbfu
Seasons don't fear The Reaper.
Nor do the wind, the sun, and the rain.
We can be like they are.

Replies are listed 'Best First'.
Re: (bbfu) Re: Messing with (my head) a substring
by psypete (Initiate) on Apr 01, 2001 at 09:28 UTC
    well, that IS the output i wanted. oh my gawd but thats a confusing replacement statement! jeez now MY head is being messed with... thank you it worked thank you! is there a simpler way of doing it? im not exactly a scholar of perl and i can pretty much understand it, but if possible id like a little more detailed explination of how it works. if you dont want to be more desctiptive, thats ok. jeez, it must have taken you about 5 seconds to make that too... and im still reading text files in line by line. (well, some). i wish i was smart, i wish i was smart, i wish i was smart...

      Well, I'm glad that I got right what you wanted. =) Now, to explain the substitution...

      my $str = "/One/Two/Three"; # Start with the whole thing in the array. You could # start with @paths = () if you'd rather not include # the current directory/path/category/whatever. my @paths = ($str); while( # Spreading this out so I can comment it... $str =~ s[ ^ # Match from the start of the string... ( # Save this in $1 / # The first / .+ # Followed by one or more <anything>'s # (including /'s!!), all the way up to # the last / (see below) ) # Don't save anything else in $1 / # The very last / in the string. This # is what causes the above .+ to stop # before the end of the string. [^/]* # We want an optional word after # the last / but NOT /'s (otherwise # it wouldn't be the last one, would it?) $ # Make sure don't stop before the end # of what's left of the string. ][$1]x # Replace it all with $1, which contains # everything up to (but not including) # the last /. Note the x option, let's us # include comments and space it out. ) { # Push the current version of $str onto the list. # It's whatever we have left after chopping off # the last word and it's /. push @paths, $str; }

      You're right. That's not a very pretty substitution. :-) If I can think of a cleaner way of doing it, I'll post it. Hope that helps.

      bbfu
      Seasons don't fear The Reaper.
      Nor do the wind, the sun, and the rain.
      We can be like they are.

      If you'd like a non regex solution, try using index and substr which, while less flexible than regexen can be clearer/better in a few situations.
      my $str = "/One/Two/Three"; my @paths = ($str); while ($paths[-1] =~ tr#/#/# > 1) { push @paths, substr($paths[-1], 0, rindex($paths[-1], '/')); } print "$_\n" for @paths;
      A few points of explanation: $paths[-1] means the last element of the array. The tr function returns the number of characters it transformed. The rindex function returns the character number where a substring last appears and the substr function returns part of a string. The push function adds items to the end of an array.

      All these things are in the documentation that comes with perl, you just have to put them together.

        Damn you. I was going to post that. :-P

        Yeah, I wouldn't have used the s/// if I'd remembered rindex. You'll have to forgive me, I was tired last night (yay 12 hours of overtime...).

        Anyway, here's my version of the substr/rindex version. It uses the same algorithm as my regexp version (ie, it's still destructive to $str) and is a little cleaner than yours (but not much):

        # MAKE SURE $str IS A _COPY_ OF YOUR DATA!!! my $str = "/One/Two/Three"; my @paths = (); while($str) { # If you don't want @push to include the current # cat (ie, "/One/Two/Three"), put this line after # the substr. But then you'll have as the last # element an empty string. Put a "last unless($str);" # between the substr and the push to avoid that. push @paths, $str; # Save everything from the begining of the string # up to, but not including, the last /. $str = substr($str, 0, rindex($str, '/')); } print "$_\n" for @paths;

        This prints:

        /One/Two/Three /One/Two /One

        bbfu
        Seasons don't fear The Reaper.
        Nor do the wind, the sun, and the rain.
        We can be like they are.