如何从特定目录获取特定扩展名的所有文件列表?

16

我正在使用这段代码来获取指定目录中所有文件的列表:

opendir DIR, $dir or die "cannot open dir $dir: $!";
my @files= readdir DIR;
closedir DIR;

我该如何修改这段代码或添加内容,使其只能查找文本文件,并且只将带有文件名前缀的数组加载进去?

目录内容示例:

.
..
923847.txt
98398523.txt
198.txt
deisi.jpg
oisoifs.gif
lksdjl.exe

示例数组内容:

files[0]=923847 
files[1]=98398523
files[2]=198

1
还可以考虑使用词法变量来处理目录句柄:opendir my $dirh, $dir_path or die "cannot open dir $dir: $!"; - Robert P
6个回答

12
my @files = glob "$dir/*.txt";
for (0..$#files){
  $files[$_] =~ s/\.txt$//;
}

有没有办法通过正则表达式将目录也提取出来?我的输出是 /dir/dir/dir/923847... 我怎样才能只获取到 923847? - CheeseConQueso
glob在这里增加了额外的工作。请参阅https://dev59.com/vnI_5IYBdhLWcg3wJfgM。 - brian d foy

6

只需要更改一行代码:

my @files= map{s/\.[^.]+$//;$_}grep {/\.txt$/} readdir DIR;

5
如果您可以使用 Perl 5.10 的新功能,这是我会如何编写它。
use strict;
use warnings;
use 5.10.1;
use autodie; # don't need to check the output of opendir now

my $dir = ".";

{
  opendir my($dirhandle), $dir;
  for( readdir $dirhandle ){ # sets $_
    when(-d $_ ){ next } # skip directories
    when(/^[.]/){ next } # skip dot-files

    when(/(.+)[.]txt$/){ say "text file: ", $1 }
    default{
      say "other file: ", $_;
    }
  }
  # $dirhandle is automatically closed here
}

如果你有非常大的目录,你可以使用一个while循环。

{
  opendir my($dirhandle), $dir;
  while( my $elem = readdir $dirhandle ){
    given( $elem ){ # sets $_
      when(-d $_ ){ next } # skip directories
      when(/^[.]/){ next } # skip dot-files

      when(/(.+)[.]txt$/){ say "text file: ", $1 }
      default{
        say "other file: ", $_;
      }
    }
  }
}

3

我找到的最简单易懂的方法(人类可读)是使用glob函数:

 # Store only TXT-files in the @files array using glob
 my @files = grep ( -f ,<*.txt>);
 # Write them out
 foreach $file (@files) {
  print "$file\n";
 }

此外,"-f" 确保只有实际的文件(而不是目录)被存储在数组中。

为什么要回滚编辑?foreach $file不是严格安全的。如果您更喜欢使用foreach而不是for,为什么不使用foreach my $file呢? - Benjamin W.
为什么要修改别人的旧代码,当你不知道它最初发布的原因呢?我甚至不知道三年前写这段代码的原因,但由于我有测试习惯,它可能完全正常运行,并且可能有一个我没有添加严格声明的原因。然而,这是否意味着我应该接受随机编辑,我是否想花时间测试它呢?当然不!这就是为什么我发现回滚更安全。 - Kebman
只是为了明确,编辑它的不是我,我只是看到了这个编辑,并认为它改善了答案。你使用词法@files然后全局$file有点奇怪。 - Benjamin W.
按照英语的方式阅读:对于文件列表中的每个文件,对列表中的每个文件执行某些操作(例如打印每个文件条目)。我喜欢英语,因为大多数人都能理解它。我仍然不会试图改进它自己,而且我将回滚所有其他尝试,因为我不想测试其他人对我的代码所做的奇怪更改。但是,如果它真的那么糟糕,我可能会完全删除我的答案。相反,请使用我的代码并发布您自己的答案。我的意思是,它不像受版权保护之类的东西,所以可以放心使用。 :) - Kebman
而且我没有添加严格声明的原因可能是有道理的。从来没有不使用strict的好理由。已经被踩了。 - Franz Kafka

2

要仅获取“.txt”文件,您可以使用文件测试运算符(-f:常规文件)和正则表达式。

my @files = grep { -f && /\.txt$/ } readdir $dir;

否则,您可以使用Perl的-T(ASCII文本文件测试运算符)来查找纯文本文件。
my @files = grep { -T } readdir $dir;

-T 用于测试是否存在“文本文件”。 - reinierpost
从操作符的perldoc页面(http://perldoc.perl.org/5.8.8/perlfunc.html)中可以看到一个好的提示:"-T File是一个ASCII文本文件(启发式猜测)"。如果他正在寻找".txt"文件,这将完全按照他的要求执行而不需要猜测。 - Robert P

1

只需使用这个:

my @files = map {-f && s{\.txt\z}{} ? $_ : ()} readdir DIR;

对我没用... ""在fileOpsDS.pl文件的第19行,名称“main::DIR”仅使用了一次:可能是拼写错误。"" ""在fileOpsDS.pl文件的第19行,尝试在无效的dirhandle DIR上执行readdir()。"" - George 2.0 Hope

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接