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

Given a complete pathname to a file. For example:
C:\Documents And Settings\My Documents\test.txt
What would be a good regex to capture the filename (test.txt) and the directory it lives in? It must also capture and *expand* the current directory if the pathname is:
./hello.txt
Thanks.

Replies are listed 'Best First'.
Re: Dissecting a complete pathname to a file
by grinder (Bishop) on Feb 27, 2007 at 23:17 UTC

    Path::Class does it all, and does it well.

    use Path::Class; my $file = './hello.txt'; my $path = Path::Class::File->new($file)->absolute; print "the file ", $path->basename", "is in the ", $path->dir, "directory\n"; # or if dealing with file specifications of a different platform... $file = "c:\\Documents and Settings\\you\\..\\me\\hello.txt"; $path = Path::Class::File->new_foreign('Win32', $file)->absolute;

    It really is the only module you need when dealing with this sort of stuff. No special cases to remember, it just works as it should.

    There is also a procedural interface if using objects isn't your thing.

    • another intruder with the mooring in the heart of the Perl

Re: Dissecting a complete pathname to a file
by Old_Gray_Bear (Bishop) on Feb 27, 2007 at 22:58 UTC
    Take a look at File::Basename; it provides fileparse(), a function that splits a full specification into three parts: (base-name, path, suffix). But, it doesn't "capture and *expand* the current directory" when fed a '.'. For that, you have to have a special case:
    use strict; use warnings; use File::Basename; use Cwd; my $file_name = './hello.world'; # Insert your file-name gathering logic here # Place the results in $file_name. my ($base_name, $path, $suffix) = fileparse($file_name); if ($path =~ /\./) { $path = cwd(); }
    Note: Written but not run past a Perl interpreter....

    ----
    I Go Back to Sleep, Now.

    OGB

      Why handle only one very specific relative path? Fix:

      use strict; use warnings; use File::Basename qw( fileparse ); use File::Spec::Functions qw( rel2abs ); my $file_path = './hello.world'; my ($file_name, $dir_path) = fileparse(rel2abs($file_path)); print("dir path: $dir_path\n"); print("file name: $file_name\n");
Re: Dissecting a complete pathname to a file
by mreece (Friar) on Feb 27, 2007 at 22:51 UTC
    consider:
    use File::Spec; my ($vol, $path, $file) = File::Spec->splitpath( File::Spec->rel2abs($filespec) );
    (rel2abs uses Cwd::cwd in this case)

      Not quite there. Then you have to join $vol and $path together, and File::Spec doesn't provide the means to do that. You might say "But catpath!", but using catpath without a file name is undocumented, and it leaves an errant trailing slash on my system. Rather than using the fragile

      use File::Spec::Functions qw( splitpath catpath rel2abs canonpath ); my ($v, $p, $file_name) = splitpath(rel2abs($file_path)); my $dir_path = canonpath(catpath($v, $p));

      use the module designed for the task at hand, File::Basename.

      use File::Basename qw( fileparse ); use File::Spec::Functions qw( rel2abs ); my ($file_name, $dir_path) = fileparse(rel2abs($file_path));
Re: Dissecting a complete pathname to a file
by GrandFather (Saint) on Feb 27, 2007 at 22:56 UTC

    Take a look at splitpath in File::Spec


    DWIM is Perl's answer to Gödel
Re: Dissecting a complete pathname to a file
by jettero (Monsignor) on Feb 27, 2007 at 22:12 UTC

    I'm pretty sure pure regular expressions isn't really the best answer here.

    If you wish to do things like expand the ./, have a look at Cwd (e.g., $full = abs_path($file)). It has all that magic built in. For finding the test.txt (assuming / is the only magic character in your paths), you probably just want something like my $short = $1 if $file =~ m/([^\\]+)\z/;.

    UPDATE: I wasn't trying to complete a programming assignment, I was just giving general suggestions... My suggestion was that filling out the fullpath is probably not the job of a regular expression. Clearly, File::Spec (below) is a cleaner solution anyway, and that doesn't involve regular expressions at all. I'm also pretty sure Cwd is a Core perl module, so I'm not sure where you're going with that.

    UPDATE2: Ahh, I see what you mean, my mistake.

    -Paul

      That returns the file. What about the second requirement to capture the directory?

      use Cwd qw( abs_path ); my $full = abs_path($file_path); my ($dir, $file) = $full =~ /^(.*)\\(.*)/; # Wrong for C:\File my ($dir, $file) = $full =~ /^(.*\\)(.*)/; # Ugly for C:\Dir\File

      A regular expression is not the way to go here. As seen below, there are better (reliable, tested, portable) options in Core Perl.

      I'm also pretty sure Cwd is a Core perl module, so I'm not sure where you're going with that.

      I didn't say Cwd was wrong. I said you only answered half the question, and that your method for answering the first half can't be expanded to answer the second. I just added two lines to the top of the snippet for clarity.