Каковы причины предпочтительности glob над readdir (или наоборот) в Perl?

Этот вопрос является побочным из этого. Некоторая история: когда я впервые изучил Perl, я почти всегда использовал glob, а не opendir + readdir, потому что мне было проще. Затем несколько различных сообщений и чтений предположили, что glob был плохим, и теперь я почти всегда использую readdir.

После размышления над этим недавним вопросом я понял, что мои причины для одного или другого выбора могут быть койками. Итак, я собираюсь выложить некоторые "за" и "против", и я надеюсь, что более опытные люди из Perl могут перезвонить и уточнить. Вопрос в двух словах - есть ли веские причины предпочесть от glob до readdir или readdir до glob (в некоторых или во всех случаях)?

glob профи:

  • Нет dotfiles (если вы не попросите их)
  • Заказ товаров гарантирован.
  • Не нужно добавлять имя каталога в элементы вручную
  • Лучшее имя (c'mon - 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 минусы:

  • Старые версии просто сломаны (но "старше" означает pre 5.6, я думаю, и, честно говоря, если вы используете pre 5.6 Perl, у вас больше проблем)
  • Вызывает stat каждый раз (т.е. бесполезное использование stat в большинстве случаев).
  • Проблемы с пробелами в именах каталогов (это все еще верно?)
  • (От брайанского ответа) Может возвращать имена файлов, которые не существуют:

    $ perl -le 'print glob "{ab}{cd}"'
    

readdir профи:

  • (Из ответа brian) opendir возвращает дескриптор файла, который вы можете передать в своей программе (и повторное использование), но glob просто возвращает список
  • (Из ответа Брайана) readdir является правильным итератором и предоставляет функции rewinddir, seekdir, telldir
  • Быстрее? (Чистая догадка, основанная на некоторых из функций glob сверху. Я так не волнуюсь об этом уровне оптимизации, но это теоретический профессионал.)
  • Меньше подвержены ошибкам краевого случая, чем glob?
  • По умолчанию (dotfiles) также читается (это тоже).
  • Может убедить вас не называть файл 0 (а также - см. ответ Брэда)
  • Кто-нибудь? Bueller? Bueller?

readdir минусы:

  • Если вы не помните, чтобы добавить имя каталога, вы будете получать бит, когда вы пытаетесь делать файловые тесты или копировать элементы или редактировать элементы или...
  • Если вы не помните grep из элементов . и .., вы будете получать бит при подсчете элементов или пытаться идти рекурсивно вниз по дереву файлов или...
  • Я уже упоминал о предложении имени каталога? (Sidenote, но мой самый первый пост в списке почтовых сообщений Perl Beginners был классическим: "Почему этот код с участием файловых задач не работает некоторое время?" Проблема, связанная с этой ошибкой. По-видимому, я все еще горький.)
  • Элементы возвращаются в определенном порядке. Это означает, что вам часто приходится помнить, чтобы сортировать их в некотором роде. (Это может быть про, если это означает большую скорость, и если это означает, что вы действительно думаете о том, как и если вам нужно сортировать элементы.) Изменить: Horrifically small sample, но на Mac readdir возвращает элементы в алфавитном порядке, без учета регистра. В поле Debian и на сервере OpenBSD заказ является абсолютно случайным. Я протестировал Mac с Apple встроенным Perl (5.8.8) и моим собственным скомпилированным 5.10.1. Поле Debian - 5.10.0, как и машина OpenBSD. Интересно, это проблема файловой системы, а не Perl?
  • Считывает все (по умолчанию также и dotfiles) (это тоже pro)
  • Не обязательно иметь дело с файлом с именем 0 (см. профи также - см. ответ Брэда)

Ответ 1

Вы пропустили самую важную, самую большую разницу между ними: 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}"'
    

Ответ 2

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♦

Ответ 3

Вот недостаток для 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.

Ответ 4

glob позволяет удобно читать все подкаталоги данной фиксированной глубины, как в glob "*/*/*". Я нашел это в нескольких случаях.

Ответ 5

Хорошо, вы в значительной степени его покрываете. Все, что учитывалось, я хотел бы использовать glob, когда я собираю быстрый одноразовый script, и его поведение - именно то, что я хочу, и использую opendir и readdir в текущем производственном коде или библиотеки, где я могу занять свое время и яснее, более чистый код полезен.

Ответ 6

Для небольших простых вещей я предпочитаю glob. Как раз на днях я использовал его и двадцать строк perl script, чтобы отследить большую часть моей музыкальной библиотеки. glob, однако, имеет довольно странное имя. Glob? Это не интуитивно понятно, насколько это название.

Мое самое большое зависание с readdir заключается в том, что он обрабатывает каталог таким образом, что это несколько странно для большинства людей. Обычно программисты не думают о каталоге как потоке, они думают об этом как о ресурсе или списке, который предоставляет glob. Название лучше, функциональность лучше, но интерфейс по-прежнему оставляет желать лучшего.

Ответ 7

Это был довольно полный список. readdirreaddir + grep) имеет меньше накладных расходов, чем glob, и поэтому это плюс для readdir, если вам нужно проанализировать множество и множество каталогов.

Ответ 8

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("*");

Ответ 9

Сначала сделайте некоторое чтение. Глава 9.6. Perl Cookbook описывает точку, которую я хочу получить, как раз под заголовком обсуждения.

Во-вторых, выполните поиск glob и dosglob в вашем каталоге Perl. Хотя многие источники (способы получить список файлов) могут быть использованы, причина, по которой я указываю вам на dosglob, заключается в том, что если вы оказались на платформе Windows (и используя решение dosglob), это на самом деле используя opendir/readdir/closedir. Другие версии используют встроенные команды оболочки или предварительно скомпилированные исполняемые файлы ОС.

Если вы знаете, что используете целевую платформу, вы можете использовать эту информацию в своих интересах. Для справки я рассмотрел это на Strawberry Perl Portable edition 5.12.2, поэтому все может немного отличаться от новых или оригинальных версий Perl.

Ответ 10

Аналогичным образом, File::Slurp имеет функцию, называемую read_dir.

Так как я использую File::Slurp другие функции в моих скриптах, read_dir также стал привычкой.

Он также имеет следующие параметры: err_mode, prefix и keep_dot_dot.