web-dev-qa-db-ja.com

Perlでreaddirよりもglobを好む(またはその逆)理由は何ですか?

この質問は これ からのスピンオフです。いくつかの歴史:私が最初にPerlを学んだとき、私はそれがより簡単だと思ったので、ほとんどいつもglob + opendirではなくreaddirを使用しました。その後、さまざまな投稿や読み物でglobが悪いことが示唆されたため、今ではほとんどの場合readdirを使用しています。

考えた後 この最近の質問 私はどちらか一方の選択の理由が二の次かもしれないことに気づきました。それで、私はいくつかの賛否両論をレイアウトするつもりです、そして私はより経験豊富なPerlの人々がチャイムを鳴らして明確にすることができることを望んでいます。一言で言えば、globよりもreaddirまたはreaddirよりもglobを好む説得力のある理由がありますか(一部またはすべての場合)?

glob長所:

  1. ドットファイルはありません(要求しない限り)
  2. アイテムの注文は保証されています
  3. アイテムにディレクトリ名を手動で追加する必要はありません
  4. より良い名前(名前だけで判断する場合、c'mon-globreaddirはコンテストではありません)
  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. 古いバージョンは単純に壊れています(しかし、「古い」は5.6より前のことを意味すると思います。率直に言って、5.6より前のPerlを使用している場合は、より大きな問題があります)
  2. 毎回statを呼び出します(つまり、ほとんどの場合、statを無用に使用します)。
  3. ディレクトリ名のスペースに関する問題(これはまだ本当ですか?)
  4. (ブライアンの回答から)存在しないファイル名を返すことができます:

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

readdir長所:

  1. (ブライアンの答えから)opendirは、プログラムで渡す(そして再利用する)ことができるファイルハンドルを返しますが、globは単にリストを返します
  2. (ブライアンの答えから)readdirは適切なイテレータであり、rewinddirseekdirtelldirに関数を提供します。
  3. もっと早く? (上からのglobの機能のいくつかに基づく純粋な推測。とにかくこのレベルの最適化についてはあまり心配していませんが、理論的なプロです。)
  4. globよりもエッジケースのバグが発生しにくいですか?
  5. デフォルトですべて(ドットファイルも)を読み取ります(これも短所です)
  6. ファイルに0という名前を付けないように説得する場合があります(欠点もあります-ブラッドの回答を参照してください)
  7. 誰でも?ビュラー?ビュラー?

readdir短所:

  1. ディレクトリ名の前に付けるのを忘れた場合は、ファイルテストを実行したり、アイテムをコピーしたり、アイテムを編集したりするときに、willビットを取得します...
  2. .および..アイテムをgrepアウトすることを覚えていない場合は、アイテムを数えるときにwillビットを取得します、またはファイルツリーを再帰的に下って歩いてみてください。
  3. ディレクトリ名を前に付けることについて言及しましたか? (補足ですが、Perl Beginnersメールリストへの私の最初の投稿は、この落とし穴に関連する古典的な「ファイルテストを含むこのコードが機能しないのはなぜですか?」という問題でした。どうやら、私はまだ苦いです。)
  4. アイテムは順不同で返送されます。つまり、何らかの方法でそれらを並べ替えることを忘れないでください。 (これは、速度が上がることを意味し、実際にアイテムを並べ替える方法と必要性について考えることを意味する場合はプロになる可能性があります。)Edit:恐ろしく小さいサンプルですが、Macではreaddirは大文字と小文字を区別せずに、アルファベット順にアイテムを返します。 DebianボックスとOpenBSDサーバーでは、順序は完全にランダムです。私はMacをAppleの組み込みPerl(5.8.8)と自分でコンパイルした5.10.1でテストしました。 Adobeボックスは、OpenBSDマシンと同様に5.10.0です。これはPerlではなくファイルシステムの問題なのだろうか?
  5. デフォルトですべて(ドットファイルも)を読み取ります(これもプロです)
  6. 0という名前のファイルを必ずしもうまく処理できるとは限りません(プロも参照してください-ブラッドの回答を参照してください)
62
Telemachus

それらの間の最も重要で最大の違いを見逃しました。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}"'
    
43
brian d foy

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♦
8
ysth

opendirreaddirの欠点は次のとおりです。

{
  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";

コードが同じ番号を2回出力すると予想されますが、0という名前のファイルがあるために出力されません。私のコンピューターでは、Perlv5.10.0およびv5.10.1でテストされた251および188が出力されます。

この問題により、ファイル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に組み込まれたパッチを送信したので、Perlv5.12.0がリリースされたときに正しく機能します。

私の修正はこれを変換します:

while( readdir $dir ){ ... }

これに:

while( defined( $_ = readdir $dir ){ ...}

これにより、readがファイルに対して機能したのと同じように機能します。実際には同じコードですが、対応するifステートメントに別の要素を追加しただけです。

6
Brad Gilbert

globを使用すると、glob "*/*/*"のように、特定の固定深度のすべてのサブディレクトリを簡単に読み取ることができます。私はこれを何度か便利だと思っています。

5
itub

まあ、あなたはほとんどそれをカバーしています。これらすべてを考慮に入れると、簡単な1回限りのスクリプトをまとめるときに、globを使用する傾向があり、その動作は私が望むものであり、opendirreaddir進行中の本番コードまたはライブラリでは、時間をかけてより明確でクリーンなコードが役立ちます。

4
chaos

小さくて単純なものについては、私はglobを好みます。つい先日、私はそれと20行のPerlスクリプトを使用して、音楽ライブラリの大部分にタグを付け直しました。ただし、globの名前はかなり奇妙です。グロブ?名前に関する限り、それはまったく直感的ではありません。

readdirとの私の最大の問題は、ほとんどの人にとってやや奇妙な方法でディレクトリを処理することです。通常、プログラマーはディレクトリをストリームとは考えず、globが提供するリソースまたはリストと見なします。名前はより良く、機能はより良いですが、インターフェースはまだ何かが望まれています。

3
Robert P

それはかなり包括的なリストでした。 readdir(およびreaddir + grep)はglobよりもオーバーヘッドが少ないため、分析する必要がある場合はreaddirにとってプラスになります。たくさんのディレクトリ。

2
mob

グロブの長所:

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
user210930

同様に、File::Slurpにはread_dirという関数があります。

スクリプトではFile::Slurpの他の関数を頻繁に使用するため、read_dirも習慣になっています。

また、次のオプションもあります:err_modeprefix、およびkeep_dot_dot

2
sam

まず、いくつかの読書をします。 9.6章。 of Perl Cookbook は、ディスカッションの見出しのすぐ下に、私がうまく行きたいポイントの概要を示しています。

次に、Perlディレクトリでglobdosglobを検索します。さまざまなソース(ファイルリストを取得する方法)を使用できますが、dosglobを指定する理由は、Windowsプラットフォームを使用している場合(およびdosglobを使用している場合)です。解決策)、実際にはopendir/readdir/closedirを使用しています。他のバージョンでは、組み込みのシェルコマンドまたはプリコンパイルされたOS固有の実行可能ファイルを使用します。

特定のプラットフォームをターゲットにしていることがわかっている場合は、この情報を活用できます。参考までに、Strawberry Perl Portableエディション5.12.2でこれを調べたので、Perlの新しいバージョンと元のバージョンでは状況が少し異なる場合があります。

1
tot