Какие есть причины предпочитать glob над readdir (или наоборот) в Perl? - PullRequest
62 голосов
/ 02 октября 2009

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

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

glob плюсы:

  1. Нет точечных файлов (если вы их не просите)
  2. Заказ товаров гарантирован
  3. Нет необходимости предварительно добавлять имя каталога на элементы вручную
  4. Лучшее имя (давай - glob против readdir - это не соревнование, если судить только по именам)
  5. (из ответа ysth; ср. 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 минусы:

  1. Старые версии просто сломаны (но, я думаю, «старые» означают pre 5.6, и, честно говоря, если вы используете pre 5.6 Perl, у вас есть большие проблемы)
  2. Каждый раз звонит stat (т. Е. Бесполезное использование stat в большинстве случаев).
  3. Проблемы с пробелами в именах каталогов (это все еще верно?)
  4. (из ответа Брайана) Может возвращать имена файлов, которые не существуют:

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

readdir плюсы:

  1. (из ответа Брайана) opendir возвращает дескриптор файла, который вы можете передать в своей программе (и повторно использовать), но glob просто возвращает список
  2. (из ответа Брайана) readdir является правильным итератором и предоставляет функции для rewinddir, seekdir, telldir
  3. Быстрее? (Чистое предположение, основанное на некоторых особенностях glob, приведенных выше. В любом случае, я не очень беспокоюсь об этом уровне оптимизации, но это теоретический аргумент.)
  4. Менее подвержен ошибкам в крайнем случае, чем glob?
  5. По умолчанию читает все (тоже дотфайлы) (это тоже мошенничество)
  6. Может убедить вас не называть файл 0 (также: см. Ответ Брэда)
  7. Кто-нибудь? Bueller? Bueller

readdir минусы:

  1. Если вы не помните добавление имени каталога, вы получите бит при попытке выполнить тестирование файла или скопировать элементы или отредактировать элементы или ...
  2. Если вы не помните grep из . и .. элементов, вы получите бит при подсчете элементов или попытаетесь рекурсивно пройти по дереву файлов или. ..
  3. Я упоминал о добавлении имени каталога? (Замечание, но моя самая первая публикация в списке рассылки Perl Beginners была классической: «Почему этот код, включающий тесты файлов, не работает время от времени?», Связанный с этой ошибкой. Очевидно, я все еще горький.)
  4. Товары возвращаются в произвольном порядке. Это означает, что вам часто придется помнить, чтобы сортировать их каким-либо образом. (Это может быть профессионалом, если это означает большую скорость, и если это означает, что вы на самом деле думаете о том, как и если вам нужно сортировать элементы.) Редактировать : ужасно маленький образец, но на Mac readdir возвращает элементы в алфавитном порядке, без учета регистра. На коробке Debian и сервере OpenBSD порядок абсолютно случайный. Я протестировал Mac со встроенным в Apple Perl (5.8.8) и скомпилировал 5.10.1. Окно Debian 5.10.0, как и машина OpenBSD. Интересно, это проблема файловой системы, а не Perl?
  5. По умолчанию читает все (тоже дотфайлы) (это тоже профи)
  6. Не обязательно хорошо работает с файлом с именем 0 (см. Также плюсы - см. Ответ Брэда)

Ответы [ 10 ]

43 голосов
/ 02 октября 2009

Вы пропустили самое важное, самое большое различие между ними: glob возвращает вам список, а opendir дает вам указатель каталога. Вы можете передать этот дескриптор каталога, чтобы другие объекты или подпрограммы могли его использовать. С помощью дескриптора каталога подпрограмма или объект не должны ничего знать о том, откуда они пришли, кто еще их использует и так далее:

 sub use_any_dir_handle {
      my( $dh ) = @_;
      rewinddir $dh;
      ...do some filtering...
      return \@files;
      }

С помощью dirhandle у вас есть управляемый итератор, в котором вы можете перемещаться с помощью seekdir, хотя с glob вы просто получаете следующий элемент.

Как и во всем, затраты и выгоды имеют смысл только применительно к определенному контексту. Они не существуют вне определенного использования. У вас есть превосходный список их различий, но я бы не классифицировал эти различия, не зная, что вы пытались с ними сделать.

Некоторые другие вещи, которые нужно запомнить:

  • Вы можете реализовать свой собственный глобус с помощью opendir, но не наоборот.

  • glob использует собственный подстановочный синтаксис, и это все, что вы получаете.

  • glob может возвращать имена файлов, которые не существуют:

    $ perl -le 'print glob "{ab}{cd}"'
    
8 голосов
/ 02 октября 2009

глобусы: могут возвращать «имена файлов», которые не существуют:

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♦
6 голосов
/ 02 марта 2010

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

5 голосов
/ 02 марта 2010

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

4 голосов
/ 02 октября 2009

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

3 голосов
/ 02 октября 2009

Для маленьких, простых вещей я предпочитаю glob. Буквально на днях я использовал его и 20-строчный Perl-скрипт для повторного добавления большой части моей музыкальной библиотеки. glob, однако, имеет довольно странное имя. Glob? Это не совсем интуитивно, насколько имя идет.

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

2 голосов
/ 07 июня 2012

На аналогичной ноте File::Slurp имеет функцию с именем read_dir.

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

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

2 голосов
/ 14 ноября 2009

Глобус Плюсы:

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("*");
2 голосов
/ 02 октября 2009

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

1 голос
/ 19 мая 2011

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

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

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

...