Каков наиболее эффективный способ открыть / действовать на все файлы в каталоге? - PullRequest
4 голосов
/ 13 июня 2011

Мне нужно выполнить мой скрипт (поиск) по всем файлам каталога. Вот методы, которые работают. Я просто спрашиваю, что лучше. (Мне нужны имена файлов формы: parsedchpt31_4.txt)

Глоб:

my $parse_corpus; #(for all options)
##glob (only if all files in same directory as script?):
my @files = glob("parsed"."*.txt");
foreach my $file (@files) {
    open($parse_corpus, '<', "$file") or die $!;
     ... all my code...
}

Readdir с условиями и временем:

##readdir:
my $dir = '.';
opendir(DIR, $dir) or die $!;

while (my $file = readdir(DIR)) {
    next unless (-f "$dir/$file"); ##Ensure it's a file
    next unless ($file =~ m/^parsed.*\.txt/); ##Ensure it's a parsed file
    open($parse_corpus, '<', "$file") or die "Couldn't open directory $!";
     ... all my code...
}

Readdir с foreach и grep:

##readdir+grep:
my $dir = '.';
    opendir(DIR, $dir) or die $!;    
foreach my $file (grep {/^parsed.*\.txt/} readdir (DIR)) {
    next unless (-f "$dir/$file"); ##Ensure it's a file
    open($parse_corpus, '<', "$file") or die "Couldn't open directory $!";
    ... all my code...
}

File :: Find:

##File::Find
my $dir = "."; ##current directory: could be (include quotes): '/Users/jon/Desktop/...'
my @files;
find(\&open_file, $dir); ##built in function
sub open_file {
    push @files, $File::Find::name if(/^parsed.*\.txt/);
}
foreach my $file (@files) {
    open($parse_corpus, '<', "$file") or die $!;
     ...all my code...
} 

Есть ли другой способ? Хорошо ли заключать весь цикл в циклы? Это нормально, я не пользуюсь closedir? Я передаю это другим, я не уверен, где будут находиться их файлы (возможно, не сможет использовать glob)

Большое спасибо, надеюсь, это правильное место, чтобы спросить это.

Ответы [ 3 ]

4 голосов
/ 13 июня 2011

Лучший или наиболее эффективный подход зависит от ваших целей и общего контекста.Ты имеешь в виду лучшую скорость, простоту кода или что-то еще?Я скептически отношусь к тому, что соображения памяти должны определять этот выбор.Сколько файлов в каталоге?

Для практичности подход glob работает довольно хорошо.Прежде чем прибегнуть к чему-то более сложному, я бы спросил, есть ли проблема.

Если вы можете использовать другие модули, другой подход заключается в том, чтобы позволить кому-то еще беспокоиться о грязных деталях:

use File::Util qw();
my $fu = File::Util->new;
my @files = $fu->list_dir($dir, qw(--with-paths --files-only));

Обратите внимание, что File::Find выполняет рекурсивный поиск по всем подкаталогам.Много раз вы не хотите или не нуждаетесь в этом.

Я также добавил бы, что мне не нравятся ваши два readdir примера, потому что они объединяют разные части функциональности: (1) получение имен файлов и (2) обработкаотдельные файлы.Я бы держал эти работы отдельно.

my $dir = '.';
opendir(my $dh, $dir) or die $!; # Use a lexical directory handle.
my @files = 
    grep { -f }
    map  { "$dir/$_" }
    grep { /^parsed.*\.txt$/ }
    readdir($dh);

for my $file (@files){
    ...
}
1 голос
/ 14 июня 2011

Я считаю, что рекурсивная функция обхода каталога с использованием идеальных партнеров opendir / readdir и File::chdir (мой любимый модуль CPAN, отлично подходит для кроссплатформенности)позволяет легко и четко манипулировать чем угодно в каталоге, включая подкаталоги, если это необходимо (если нет, пропустите рекурсию).

Пример (простой глубокий ls):

#!/usr/bin/env perl
use strict;
use warnings;

use File::chdir; #Provides special variable $CWD
# assign $CWD sets working directory
# can be local to a block
# evaluates/stringifies to absolute path
# other great features

walk_dir(shift);

sub do_something {
  print shift . "\n";
}

sub walk_dir {
  my $dir = shift;
  local $CWD = $dir;
  opendir my $dh, $CWD; # lexical opendir, so no closedir needed
  print "In: $CWD\n";

  while (my $entry = readdir $dh) {
    next if ($entry =~ /^\.+$/);
    # other exclusion tests    

    if (-d $entry) {
      walk_dir($entry);
    } elsif (-f $entry) {
      do_something($entry);
    }
  }

}
1 голос
/ 13 июня 2011

Я думаю, что использование цикла while является более безопасным ответом. Зачем? Поскольку загрузка всех имен файлов в массив может означать большое использование памяти, а использование построчных операций позволяет избежать этой проблемы.

Я предпочитаю от readdir до glob, но это скорее вопрос вкуса.

Если производительность является проблемой, можно сказать, что проверка -f не нужна для любого файла с расширением .txt.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...