Этот вопрос является побочным из этого. Некоторая история: когда я впервые изучил Perl, я почти всегда использовал glob
, а не opendir
+ readdir
, потому что мне было проще. Затем несколько различных сообщений и чтений предположили, что glob
был плохим, и теперь я почти всегда использую readdir
.
После размышления над этим недавним вопросом я понял, что мои причины для одного или другого выбора могут быть койками. Итак, я собираюсь выложить некоторые "за" и "против", и я надеюсь, что более опытные люди из Perl могут перезвонить и уточнить. Вопрос в двух словах - есть ли веские причины предпочесть от glob
до readdir
или readdir
до glob
(в некоторых или во всех случаях)?
glob
профи:glob
versus readdir
не является конкурсом, если мы судим только по именам)(От ответа ysth, cf. glob
cons 4 ниже) Может возвращать несуществующие имена файлов:
@deck = glob "{A,K,Q,J,10,9,8,7,6,5,4,3,2}{\x{2660},\x{2665},\x{2666},\x{2663}}";
glob
минусы:stat
каждый раз (т.е. бесполезное использование stat
в большинстве случаев).(От брайанского ответа) Может возвращать имена файлов, которые не существуют:
$ perl -le 'print glob "{ab}{cd}"'
readdir
профи:opendir
возвращает дескриптор файла, который вы можете передать в своей программе (и повторное использование), но glob
просто возвращает списокreaddir
является правильным итератором и предоставляет функции rewinddir
, seekdir
, telldir
glob
сверху. Я так не волнуюсь об этом уровне оптимизации, но это теоретический профессионал.)glob
?0
(а также - см. ответ Брэда)readdir
минусы:grep
из элементов .
и ..
, вы будете получать бит при подсчете элементов или пытаться идти рекурсивно вниз по дереву файлов или...readdir
возвращает элементы в алфавитном порядке, без учета регистра. В поле Debian и на сервере OpenBSD заказ является абсолютно случайным. Я протестировал Mac с Apple встроенным Perl (5.8.8) и моим собственным скомпилированным 5.10.1. Поле Debian - 5.10.0, как и машина OpenBSD. Интересно, это проблема файловой системы, а не Perl?0
(см. профи также - см. ответ Брэда)Вы пропустили самую важную, самую большую разницу между ними: glob
возвращает вам список, но opendir
дает вам дескриптор каталога. Вы можете передать этот дескриптор каталога, чтобы использовать его другими объектами или подпрограммами. С помощью дескриптора каталога подпрограмма или объект не должны знать ничего о том, откуда она появилась, кто еще ее использует и т.д.:
sub use_any_dir_handle {
my( $dh ) = @_;
rewinddir $dh;
...do some filtering...
return \@files;
}
С помощью dirhandle у вас есть управляемый итератор, где вы можете перемещаться с помощью seekdir
, хотя с glob
вы просто получаете следующий элемент.
Как и в случае с чем-либо, затраты и выгоды имеют смысл только при применении к определенному контексту. Они не существуют вне конкретного использования. У вас есть отличный список их различий, но я бы не стал классифицировать эти различия, не зная, что вы пытаетесь с ними сделать.
Некоторые другие вещи, которые нужно запомнить:
Вы можете реализовать свой собственный glob с помощью opendir
, но не наоборот.
glob использует свой собственный синтаксис подстановочных знаков, и все, что вы получаете.
glob может возвращать имена файлов, которые не существуют:
$ perl -le 'print glob "{ab}{cd}"'
glob pros: Может возвращать "имена файлов", которых не существует:
my @deck = List::Util::shuffle glob "{A,K,Q,J,10,9,8,7,6,5,4,3,2}{\x{2660},\x{2665},\x{2666},\x{2663}}";
while (my @hand = splice @deck,0,13) {
say join ",", @hand;
}
__END__
6,8,7,Q,K,Q,A,3,6,5,10,Q,2
2,2,K,A,8,6,8,10,10,5,3,Q,K
5,5,J,J,J,9,2,8,9,4,10,6,3
3,A,K,4,7,4,A,4,7,J,9,7,9
cp -a dirname{,.orig}
.)
Вот недостаток для opendir
и readdir
.
{
open my $file, '>', 0;
print {$file} 'Breaks while( readdir ){ ... }'
}
opendir my $dir, '.';
my $a = 0;
++$a for readdir $dir;
print $a, "\n";
rewinddir $dir;
my $b = 0;
++$b while readdir $dir;
print $b, "\n";
Вы ожидаете, что код будет печатать одинаковый номер дважды, но это не потому, что есть файл с именем 0
. На моем компьютере он печатает 251
и 188
, протестированные с помощью Perl v5.10.0 и v5.10.1
Эта проблема также делает так, что это просто печатает пустую строку, независимо от наличия файла 0
:
use 5.10.0;
opendir my $dir, '.';
say while readdir $dir;
Где это всегда хорошо работает:
use 5.10.0;
my $a = 0;
++$a for glob '*';
say $a;
my $b = 0;
++$b while glob '*';
say $b;
say for glob '*';
say while glob '*';
Я исправил эти проблемы и отправил патч, который попал в Perl v5.11.2, так что это будет корректно работать с Perl v5.12.0, когда оно появится.
Мое исправление преобразует это:
while( readdir $dir ){ ... }
в это:
while( defined( $_ = readdir $dir ){ ...}
Что делает его работать так же, как read
работал над файлами. На самом деле это один и тот же бит кода, я просто добавил еще один элемент в соответствующие операторы if
.
. Still, I'll note it in the
минусах readdir.
each
.
glob
позволяет удобно читать все подкаталоги данной фиксированной глубины, как в glob "*/*/*"
. Я нашел это в нескольких случаях.
Хорошо, вы в значительной степени его покрываете. Все, что учитывалось, я хотел бы использовать glob
, когда я собираю быстрый одноразовый script, и его поведение - именно то, что я хочу, и использую opendir
и readdir
в текущем производственном коде или библиотеки, где я могу занять свое время и яснее, более чистый код полезен.
Для небольших простых вещей я предпочитаю glob
. Как раз на днях я использовал его и двадцать строк perl script, чтобы отследить большую часть моей музыкальной библиотеки. glob
, однако, имеет довольно странное имя. Glob? Это не интуитивно понятно, насколько это название.
Мое самое большое зависание с readdir
заключается в том, что он обрабатывает каталог таким образом, что это несколько странно для большинства людей. Обычно программисты не думают о каталоге как потоке, они думают об этом как о ресурсе или списке, который предоставляет glob. Название лучше, функциональность лучше, но интерфейс по-прежнему оставляет желать лучшего.
readdir
немного душно (как имя) и glob
только о праве. С другой стороны, мне нравится оператор splat
Ruby (как имя и так далее), поэтому я думаю, что я странный.
Это был довольно полный список. readdir
(и readdir
+ grep
) имеет меньше накладных расходов, чем glob
, и поэтому это плюс для readdir
, если вам нужно проанализировать множество и множество каталогов.
Аналогичным образом, File::Slurp
имеет функцию, называемую read_dir
.
Так как я использую File::Slurp
другие функции в моих скриптах, read_dir
также стал привычкой.
Он также имеет следующие параметры: err_mode
, prefix
и keep_dot_dot
.
Сначала сделайте некоторое чтение. Глава 9.6. Perl Cookbook описывает точку, которую я хочу получить, как раз под заголовком обсуждения.
Во-вторых, выполните поиск glob
и dosglob
в вашем каталоге Perl. Хотя многие источники (способы получить список файлов) могут быть использованы, причина, по которой я указываю вам на dosglob
, заключается в том, что если вы оказались на платформе Windows (и используя решение dosglob
), это на самом деле используя opendir
/readdir
/closedir
. Другие версии используют встроенные команды оболочки или предварительно скомпилированные исполняемые файлы ОС.
Если вы знаете, что используете целевую платформу, вы можете использовать эту информацию в своих интересах. Для справки я рассмотрел это на Strawberry Perl Portable edition 5.12.2, поэтому все может немного отличаться от новых или оригинальных версий Perl.
glob pros:
3) Не нужно добавлять имя каталога в элементы вручную
Исключение:
say for glob "*";
--output:--
1perl.pl
2perl.pl
2perl.pl.bak
3perl.pl
3perl.pl.bak
4perl.pl
data.txt
data1.txt
data2.txt
data2.txt.out
Насколько я могу судить, правилом для glob
является: вы должны предоставить полный путь к каталогу, чтобы получить полный путь назад. Документы Perl, похоже, не упоминают об этом, и ни одна из сообщений здесь не указана.
Это означает, что glob
можно использовать вместо readdir
, если вам нужны только имена файлов (а не полные пути), и вы не хотите возвращать скрытые файлы, то есть те, которые начинаются с '.'. Например,
chdir ("../..");
say for glob("*");