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

I want to create a regex to help me parse a series of filenames that might look like, for example:
/a/b/c/d/e /x/y/z/a/b/c/d/e

where I want to extract only the last 3 components of the file name
ie c, d, e and f

I thought this would do it as it was pinned at the end (using $) and I had minimised the greediness of . (using .?)
-- I have added "\.([a-z0-9]+?)" to the end to pickup files with a suffix --
/\/(.*?)\/(.*?)\/(.*?)\.([a-z0-9]+?)$/i

but for some reason it picks the wrong components.
For example, if I have
/x/y/z/a/b/c/d/e.f

I get
$1=x
$2=y
$3=e
$4=f

Code tags added by GrandFather

Replies are listed 'Best First'.
Re: Regex to extract last 3 components of filename
by choroba (Cardinal) on May 04, 2021 at 21:51 UTC
    If you don't want to match a slash, tell Perl about it:
    m{/([^/]*?)/([^/]*?)/([^/]*?)\.([a-z0-9]+?)$}

    The problem can be solved using split in a much cleaner way:

    my @parts = (split m{/}, '/x/y/z/a/b/c/d/e.f'); splice @parts, 0, -3; push @parts, split /\./, pop @parts; print "<$_>" for @parts;

    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
Re: Regex to extract last 3 components of filename
by hippo (Archbishop) on May 04, 2021 at 21:51 UTC

    Note that if I were doing this for real I would not use a regex match to accomplish it but would use split for brevity or a module to cover edge cases and portability. However, here is one solution. This might not achieve what you want because it isn't clear precisely what you do want. It should provide a starting point at least.

    use strict; use warnings; use Test::More; my @tests = ( { in => ' /x/y/z/a/b/c/d/e.f', want => '/c/d/e.f' } ); plan tests => scalar @tests; my $re = qr#((?:/[^/]*?){3})$#; for my $t (@tests) { $t->{in} =~ $re; is $1, $t->{want}, "Extracted $t->{want}"; }

    🦛

Re: Regex to extract last 3 components of filename
by 1nickt (Canon) on May 04, 2021 at 21:46 UTC

      Started to reply with a similar Path::Tiny invocation then read what you'd linked so . . . foo. Instead, File::Spec(::Functions):

      $ perl -MFile::Spec::Functions=splitdir -E '@p = splitdir( "/x/y/z/a/b +/c/d/e.f" );say join( qq{\n}, @p[-3,-2,-1] )' c d e.f

      The cake is a lie.
      The cake is a lie.
      The cake is a lie.