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

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

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

glob плюсы:

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

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

readdir плюсы:

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

readdir плюсы:

  1. если вы не помните, чтобы добавить имя каталога, вы будет получить бит при попытке сделать filetests или копировать элементы или редактировать элементы или...
  2. если вы не помните к grep на . и .. предметы, вы будет получить бит при подсчете элементов, или попытаться ходить рекурсивно вниз по дереву файлов или...
  3. сделал Я упоминал о добавлении имени каталога? (Sidenote, но мой самый первый пост в списке новичков Perl был классическим: "почему этот код с использованием filetests не работает некоторое время?- проблема, связанная с этим, попалась. Видимо, мне все еще горько.)
  4. элементы возвращаются в произвольном порядке. Это означает, что вам часто придется помнить, чтобы отсортировать их каким-то образом. (Это может быть профи, если это означает большую скорость, и если это означает, что вы на самом деле думаю о том, как и если вам нужно отсортировать элементы.) редактировать: ужасно маленький образец, но на Mac readdir возвращает элементы в алфавитном порядке, без учета регистра. На коробке Debian и сервере OpenBSD порядок совершенно случайный. Я протестировал Mac со встроенным Perl от Apple (5.8.8) и моим собственным скомпилированным 5.10.1. Коробка Debian-5.10.0, как и машина OpenBSD. Интересно, это проблема файловой системы, а не Perl?
  5. читает все (dotfiles тоже) по умолчанию (это также Pro)
  6. не обязательно хорошо справляется с файлом с именем 0 (см. также плюсы-см. ответ Брэда)

10 ответов


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

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♦

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


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


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


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

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


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


Глоб плюсы:

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 is: вы должны предоставить полный путь к каталогу, чтобы получить полные пути назад. Документы Perl, похоже, не упоминают об этом, и ни один из сообщений здесь.

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

chdir ("../..");  
say for glob("*");

на подобной ноте, File::Slurp имеет функцию read_dir.

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

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


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

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

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