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

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

Hi Perl Monks,

I have a string of filename that looks something like this :
xxx/yyy/zzz.a

My goal is to get the file name only (zzz.a).

Is there any built in function in CGI/perl to do this, or how should I do this in case there is no built in function available ?

Thanks

  • Comment on file name parsing to get the original file name

Replies are listed 'Best First'.
Re: file name parsing to get the original file name
by Abigail-II (Bishop) on Aug 19, 2003 at 11:04 UTC
    use File::Basename;

    The module comes standard with modern versions of Perl.

    Abigail

    A reply falls below the community's threshold of quality. You may see it by logging in.
Re: file name parsing to get the original file name
by arthas (Hermit) on Aug 19, 2003 at 13:24 UTC
    As Abigail-II suggested, use File::BaseName. However, if you are parsing HTML form input, beware: the module is not going to work regardless of the operating system of the client. Windows machines put backslashes (\) instead of slashes (/) for instance, so if you need to receive file uploaded by a Windows client and your program is on an Unix server, you'll need to do:

    fileparse_set_fstype("MSWin32");

    before calling the parsing function. The default FS type of the module depends on the operaing system where the script runs (it's decided upon examining the $^O variabile).

    Michele.

      My general rule of thumb is to use forward slashes wherever you can get away with it, and use OS-specific separators only when testing shows that they're required. Be ready to accept the OS-specific separators or the forward slashes whenever the user supplies them.

      I just tried this test on Windows with Perl 5.6.0. It shows that File::Basename accepts forward or backslashes on Windows. This is not a thorough test on all platforms and versions.

      use File::Basename qw(basename dirname); print basename("c:\\path\\file.txt"), $/; print basename("c:/path/file.txt"), $/; print dirname("c:\\path\\file.txt"), $/; print dirname("c:/path/file.txt"), $/; __OUTPUT__ file.txt file.txt c:\path c:/path

      "Be lenient in what you accept, strict in what you produce."

      --
      [ e d @ h a l l e y . c c ]

        Output on a Linux box is...
        c:\path\file.txt
        file.txt
        .
        c:/path
        
        ...which is obviously wrong if you need to deal with the win32 input.
      Hi Michele,

      I was trying your solution since my parsing is indeed HTML form input.Tried the other solutions but can't work, it still does not recognize the '\'

      Is there anything that I should need to add before inserting the :
      fileparse_set_fstype("MSWin32");
      Because my html shows an error when I add that line.

      Thanks

        Hi all,

        Thanks for the help, for my post just now I realized that the use File::Basename shouldn't use the qw(...)

        Thanks

Re: file name parsing to get the original file name
by bear0053 (Hermit) on Aug 19, 2003 at 13:55 UTC
    you can also do the following (maybe a little less efficient way but at least you won't need to require another module)
    my $filename = 'xxx/yyy/zzz.a'; my ($tmp1,$tmp2,$name) = split(/\//,$filename); print "$name";
    $name from above will now contain zzz.a
      That's not very flexible. I can understand not needing to care about separators from different platforms, but your approach only works if you have two directories and then the file. It will fail if there's just one directory, or three.

      my $filename = "one/two/three/four/five/six.a"; my $name = (split m{/} => $filename) [-1];

      Abigail

        If we are trying for 'best UNIX-only solution that requires no modules', I vote for:

        my($name) = $path =~ /([^\/]+)\z/;

        I second Abigail-II's suggestion that a module is used, though, as these sorts of problems are generic in nature, and it is very scary to see hundreds of different solutions to the same problem, each with their own independent set of failings.

        At least if a single module is used by everybody, then the code is being excercised in a higher percentage of the possible contexts, and problems will be fixed sooner, rather than being discovered much later.

        UPDATE: Optimizing the above expression, we can see the speed improve by a factor of 6:

        $path =~ /(?:.*\/)?(.+)/s; my $name = $1;

        It seems that the Perl regular expression engine does a poor job of dealing with matching a pattern at the end of a string. This is not surprising given that most regular expression engines start searching from the beginning of the string.