遍历文件夹的方法比较

198 阅读2分钟

本贴对三种遍历文件夹方法比较。
1. 使用File::Find;
2. 递归遍历。(遍历函数为lsr)
3. 使用队列或栈遍历。(遍历函数为lsr_s)

1.use File::Find\

  1. #!/usr/bin/perl -W\

  2. #\

  3. # File: find.pl\

  4. # Author: 路小佳\

  5. # License: GPL-2\

  6. \

  7. use strict;\

  8. use warnings;\

  9. use File::Find;\

  10. \

  11. my ($size, $dircnt, $filecnt) = (0, 0, 0);\

  12. \

  13. sub process {

    \

  14. my $file = $File::Find::name;\

  15. #print $file, "\n";\

  16. if (-d $file) {

    \

  17. $dircnt++;\

  18. }\

  19. else {

    \

  20. $filecnt++;\

  21. $size += -s $file;\

  22. }\

  23. }\

  24. \

  25. find(\&process, '.');\

  26. print "$filecnt files, $dircnt directory. $size bytes.\n";



2. lsr递归遍历\

  1. #!/usr/bin/perl -W\

  2. #\

  3. # File: lsr.pl\

  4. # Author: 路小佳\

  5. # License: GPL-2\

  6. \

  7. use strict;\

  8. use warnings;\

  9. \

  10. sub lsr($) {

    \

  11. sub lsr;\

  12. my $cwd = shift;\

  13. \

  14. local *DH;\

  15. if (!opendir(DH, $cwd)) {

    \

  16. warn "Cannot opendir $cwd: $! $^E";\

  17. return undef;\

  18. }\

  19. foreach (readdir(DH)) {

    \

  20. if ($_ eq '.' || $_ eq '..') {

    \

  21. next;\

  22. }\

  23. my $file = $cwd.'/'.$_;\

  24. if (!-l $file && -d _) {

    \

  25. $file .= '/';\

  26. lsr($file);\

  27. }\

  28. process($file, $cwd);\

  29. }\

  30. closedir(DH);\

  31. }\

  32. \

  33. my ($size, $dircnt, $filecnt) = (0, 0, 0);\

  34. \

  35. sub process($$) {

    \

  36. my $file = shift;\

  37. #print $file, "\n";\

  38. if (substr($file, length($file)-1, 1) eq '/') {

    \

  39. $dircnt++;\

  40. }\

  41. else {

    \

  42. $filecnt++;\

  43. $size += -s $file;\

  44. }\

  45. }\

  46. \

  47. lsr('.');\

  48. print "$filecnt files, $dircnt directory. $size bytes.\n";



3. lsr_s栈遍历\

  1. #!/usr/bin/perl -W\

  2. #\

  3. # File: lsr_s.pl\

  4. # Author: 路小佳\

  5. # License: GPL-2\

  6. \

  7. use strict;\

  8. use warnings;\

  9. \

  10. sub lsr_s($) {

    \

  11. my $cwd = shift;\

  12. my @dirs = ($cwd.'/');\

  13. \

  14. my ($dir, $file);\

  15. while ($dir = pop(@dirs)) {

    \

  16. local *DH;\

  17. if (!opendir(DH, $dir)) {

    \

  18. warn "Cannot opendir $dir: $! $^E";\

  19. next;\

  20. }\

  21. foreach (readdir(DH)) {

    \

  22. if ($_ eq '.' || $_ eq '..') {

    \

  23. next;\

  24. }\

  25. $file = $dir.$_;\

  26. if (!-l $file && -d _) {

    \

  27. $file .= '/';\

  28. push(@dirs, $file);\

  29. }\

  30. process($file, $dir);\

  31. }\

  32. closedir(DH);\

  33. }\

  34. }\

  35. \

  36. my ($size, $dircnt, $filecnt) = (0, 0, 0);\

  37. \

  38. sub process($$) {

    \

  39. my $file = shift;\

  40. print $file, "\n";\

  41. if (substr($file, length($file)-1, 1) eq '/') {

    \

  42. $dircnt++;\

  43. }\

  44. else {

    \

  45. $filecnt++;\

  46. $size += -s $file;\

  47. }\

  48. }\

  49. \

  50. lsr_s('.');\

  51. print "$filecnt files, $dircnt directory. $size bytes.\n";




对我的硬盘/dev/hda6的测试结果。

1: File::Find\

  1. 26881 files, 1603 directory. 9052479946 bytes.\

  2. \

  3. real 0m9.140s\

  4. user 0m3.124s\

  5. sys 0m5.811s

复制代码



2: lsr\

  1. 26881 files, 1603 directory. 9052479946 bytes.\

  2. \

  3. real 0m8.266s\

  4. user 0m2.686s\

  5. sys 0m5.405s

复制代码



3: lsr_s\

  1. 26881 files, 1603 directory. 9052479946 bytes.\

  2. \

  3. real 0m6.532s\

  4. user 0m2.124s\

  5. sys 0m3.952s

复制代码


测试时考虑到cache所以要多测几次取平均, 也不要同时打印文件名, 因为控制台是慢设备, 会形成瓶颈。
lsr_s之所以用栈而不是队列来遍历,是因为Perl的push shift pop操作是基于数组的, push pop这样成对操作可能有优化。内存和cpu占用大小顺序也是1>2>3.\

  1. CPU load memory\

  2. use File::Find 97% 4540K\

  3. lsr 95% 3760K\

  4. lsr_s 95% 3590K

复制代码


结论: 强烈推荐使用lsr_s来遍历文件夹。

=============再罗嗦几句======================
从执行效率上来看,find.pl比lsr.pl的差距主要在user上, 原因是File::Find模块选项较多, 条件判断费时较多,而lsr_s.pl比lsr.pl在作系统调用用时较少, 是因为递归时程序还在保存原有的文件句柄和函数恢复现场的信息, 所以sys费时较多。 所以lsr_s在sys与user上同时胜出是不无道理的。\