Perl readdir по порядку

Есть ли способ гарантировать порядок из списка, возвращаемого readdir?

У меня есть код:

opendir(my $DIR, $src) or die "Error opening $src";

# Loop for each file in the directory
while (my $file = readdir($DIR))
{
        print "$file\n";
    }

Но он возвращается в случайном порядке. Теперь я знаю, что существует множество решений с помощью быстрого поиска в Google, но я не могу найти нужный мне порядок. В основном я хочу, чтобы папки отображались ПЕРВЫМИ или ПОСЛЕДНИМИ, а не между файлами.

Например, прямо сейчас, если у меня есть структура папок:

folder
folder
file1
file2
file3

Я получаю результат:

file2
folder
folder
file1
file3

Когда я действительно хочу:

folder
folder
file1
file2
file3

Or:

file1
file2
file3
folder
folder

Любой способ добиться этого?


person Travv92    schedule 28.05.2013    source источник
comment
какой-то вид должен быть использован.   -  person mpapec    schedule 28.05.2013


Ответы (4)


Вы можете сортировать, сначала помещая папки, а затем сортируя по имени файла/каталога,

# $src pointing to folder open with opendir
my @sorted_dir = 
  map $_->[0],
  sort {
    $a->[1] <=> $b->[1]
      ||
    $a->[0] cmp $b->[0]
  }
  map [ $_, -f "$src/$_" ],
  readdir($DIR);

Хотя аналогичный эффект может быть достигнут с помощью

for my $file (sort { -f "$src/$a" <=> -f "$src/$b" } readdir($DIR)) {
  print "$file\n";
}

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

person mpapec    schedule 28.05.2013
comment
Спасибо, я надеялся на более элегантное решение. Я вернусь к этому, если ничего не получится :) - person Travv92; 28.05.2013
comment
Обновлено более простой, но более медленной альтернативой. - person mpapec; 28.05.2013
comment
Спасибо, это красивее. Это также было проблемой с другим решением, предоставленным simbabque, в нем отсутствовал $src/, и поэтому оно не работало! - person Travv92; 28.05.2013
comment
Также обратите внимание, что второй не сортируется по именам записей. - person mpapec; 28.05.2013

Для этого можно использовать sort, просматривая каждую запись списка, возвращаемого readdir.

opendir(my $DIR, '.') or die "Error opening ";

foreach my $file (sort { -d $a <=> -d $b } readdir($DIR)) {
  print "$file\n";
}

Это даст папки в последнюю очередь.

person simbabque    schedule 28.05.2013
comment
Привет, когда я это делаю, я получаю Use of uninitialized value in numeric comparison (<=>). - person Travv92; 28.05.2013
comment
Попробуйте cmp вместо <=>. - person innaM; 28.05.2013
comment
Теперь я получаю Use of uninitialized value in string comparison (cmp). Удаление -d из обоих не приводит к ошибкам (но, очевидно, неправильному выводу). - person Travv92; 28.05.2013
comment
Похоже, в возвращаемых значениях readdir есть пустой материал. <=> правильно, кстати. cmp является буквенно-цифровым, а -d возвращает только 0 и 1, поэтому cmp не требуется. - person simbabque; 28.05.2013

foreach (sort readdir $dh) {} отлично работает для меня.

Например:

opendir (my $DIR, "$dir") || die "Error while opening $dir: $!\n";

foreach my $dirFileName(sort readdir $DIR)
{
      next if $dirFileName eq '.' or $dirFileName eq '..';
      print("fileName: $dirFileName ... \n");
}
person Hauke    schedule 19.03.2015

Вы можете использовать part из List::MoreUtils.

#!/usr/bin/env perl

use strict;
use warnings;

use List::MoreUtils 'part';

my $dir = shift || '.';

opendir my $dh, $dir or die "Cannot open $dir";

my ($files, $dirs) = part { -d } sort readdir $dh;

print "$_\n" for @$files, @$dirs;

Еще один вариант — File::Next.

person Joel Berger    schedule 28.05.2013
comment
Он работает, его легко читать, и он почти наверняка у вас уже есть. Но... на ваш выбор. - person Joel Berger; 28.05.2013