Как проверить, содержит ли имя файла информацию каталога?

Я хотел бы проверить, есть ли в имени файла какая-либо информация о каталоге, желательно без использования системно-зависимого хака, такого как index($file_name,'/')!=-1, Я знаю о File::Spec модуль, но не могу найти способ использовать этот модуль, чтобы сделать это определение. Возможно, другой модуль будет работать лучше. Вот несколько примеров имен файлов:

# This does not have directory info, so the test should fail (i.e., return false)
my $file_name1='abc.txt';

# These do have directory info, so the test should succeed (i.e., return true)
my $file_name2='dir/abc.txt';
my $file_name3='/home/me/abc.txt';
my $file_name4='~me/abc.txt';

4 ответа

Решение

splitdir вернет список. Оценивается в скалярном контексте, возвращает количество элементов в списке. Если в списке более 1 элемента, то вы знаете, что существует разделитель каталогов.

use warnings;
use strict;
use File::Spec qw();

while (<DATA>) {
    chomp;
    if (File::Spec->splitdir($_) > 1) {
        print 'PASS';
    }
    else {
        print 'FAIL';
    }
    print ": $_\n";
}

__DATA__
abc.txt
dir/abc.txt
/home/me/abc.txt
~me/abc.txt

Печать:

FAIL: abc.txt
PASS: dir/abc.txt
PASS: /home/me/abc.txt
PASS: ~me/abc.txt

ПРИМЕР

($volume,$directories,$file) =
                       File::Spec->splitpath( $path );

splitpath Разбивает путь на части тома, каталога и имени файла. В системах без понятия объема возвращает "для объема".

Вы должны использовать File::Spec чтобы получить правильно переносимый результат.

Если сложность вас беспокоит, вы должны написать свою собственную библиотеку File::Spec функции.

Это может выглядеть так

файл PathLib.pm

package PathLib;
use strict;
use warnings;

require File::Spec;
require Exporter;

our @ISA       = qw(Exporter);
our @EXPORT_OK = qw(has_path);

sub has_path {
  my ($volume, $path, $name) = File::Spec->splitpath($_[0]);
  return ($path or $volume)
}

и вы могли бы использовать это так. (В настоящее время я работаю на ноутбуке с Windows и не могу проверить версию Unix, пока не доберусь до рабочего стола, но я уверен, что это сработает для вас.)

use strict;
use warnings;

use PathLib qw(has_path);

my @paths = qw{
  C:\aa\bb\cc.txt
  E:ee.txt
  cc.txt
  ee\
};

for (@paths) {
  printf "%6s - %s\n", has_path($_) ? 'true' : 'false', $_;
}

выход

  true - C:\aa\bb\cc.txt
  true - E:ee.txt
 false - cc.txt
  true - ee\

В Perl / всегда зарезервирован как разделитель каталогов (за исключением старой системы Mac OS [6-9], которая настаивала на том, чтобы она была :). Однако в других операционных системах могут использоваться и другие символы. Например, на компьютерах с Windows оба они ссылаются на один и тот же каталог:

$file = 'C:\this\that\foo.txt';
$file = "C:/this/that/foo.txt";

Таким образом, вы всегда должны подозревать, что / это каталог, если вы не на старом Mac. Кроме того, вы должны выяснить, каким может быть текущий разделитель путей в этой системе, и найти его тоже.

Я надеялся, что File::Spec или File::Path могут иметь какую-либо функцию или метод, который показывает имя предпочтительного разделителя каталогов, но это не документировано.

Просматривая код различных File::Spec субмодули, я вижу, что фактический разделитель файлов жестко запрограммирован, а функции являются более сложными, чем я первоначально думал. Никаких специальных скрытых функций. Убирайся.

Тем не менее, хорошая новость заключается в том, что File::Spec это стандартный модуль, поэтому нет причин бояться его. Он даже был включен в Perl 5.8.8. Вам не нужно беспокоиться, что он может быть не установлен.

Я сделал простой тест:

#! /usr/bin/env perl
#
use strict;
use warnings;
use feature qw(say);

use File::Spec;

while ( my $file = <DATA> ) {
    chomp $file;
    my ( $volume, $directory, $file )  = File::Spec->splitpath( $file );
    if ( $directory ) {
        say qq('$volume' '$directory' '$file' contains directory stuff);
    }
    else {
        say qq('$volume' '$directory' '$file' is pure file);
    }
}


__DATA__
Foo/Bar.txt
/Bar.txt
Bar.txt/
Foo_Bar.txt

Результаты были:

'' 'Foo/' 'Bar.txt' contains directory stuff
'' '/' 'Bar.txt' contains directory stuff
'' 'Bar.txt/' '' contains directory stuff
'' '' 'Foo_Bar.txt' is pure file

Кажется, что каждый раз, когда появляется разделитель каталогов, строка считается файлом. Я думаю, это имеет смысл.

Это одна из тех задач, которые в конечном итоге оказываются более сложными, чем вы себе представляете (например, определение, является ли строка действительным адресом электронной почты или номером). Лучше всего использовать File::Spec->split и убедитесь, что там нет тома или каталога.

Другие вопросы по тегам