Python统计一个目录及其所有子目录中的文件数。

4

我正在尝试计算一个文件夹及其所有子文件夹中的所有文件数量。 例如,如果我的文件夹看起来像这样:

file1.txt
subfolder1/
├── file2.txt
├── subfolder2/
│   ├── file3.txt
│   ├── file4.txt
│   └── subfolder3/
│       └── file5.txt
└── file6.txt
file7.txt

我希望您能提供数字7。
我尝试的第一件事是使用递归函数计算所有文件并对每个文件夹调用自身。
def get_file_count(directory: str) -> int:

    count = 0

    for filename in os.listdir(directory):

        file = (os.path.join(directory, filename))

        if os.path.isfile(file):
            count += 1

        elif os.path.isdir(file):
            count += get_file_count(file)

    return count

这种方法可行,但对于大目录需要很长时间。
我还记得这篇文章,它展示了使用win32com快速计算文件夹总大小的方法,我想知道这个库是否也提供了我正在寻找的功能。但是在搜索后,我只找到了这个。
fso = com.Dispatch("Scripting.FileSystemObject")
folder = fso.GetFolder(".")
size = folder.Files.Count

但是这只返回目标文件夹中文件的数量(不包括其子文件夹中的文件)。
那么,你知道在Python中是否有一种最优的函数可以返回文件夹及其所有子文件夹中文件的数量吗?

你尝试过合并这两个实现吗? - weasel
1
这里可能会用到os.walk - Scott Hunter
不,@weasel,你认为这会减少执行时间吗? - crazycat256
你能否通过搜索涉及的文件数量举例量化“很长时间”?我刚刚使用*glob()*进行了测试,分析了一个深度嵌套的目录结构,在2.5秒内揭示了超过160,000个普通文件。 - DarkKnight
第一个函数在40秒内找到了61,000个文件 @LancelotduLac - crazycat256
7个回答

2
如果我理解得正确,您只需要执行以下操作:
sum(len(files) for _, _, files in os.walk('path/to/folder'))

或者,为了可能稍微更好的性能而避免使用len

sum(1 for _, _, files in os.walk('folder_test') for f in files)

1

这段代码将显示指定根目录下所有不是目录(例如普通文件、符号链接)的目录条目数量。

代码还包括时间计算和用于测试的实际路径名:

from glob import glob, escape
import os
import time


def get_file_count(directory: str) -> int:
    count = 0
    for filename in glob(os.path.join(escape(directory), '*')):
        if os.path.isdir(filename):
            count += get_file_count(filename)
        else:
            count += 1
    return count

start = time.perf_counter()
count = get_file_count('/Volumes/G-DRIVE Thunderbolt 3')
end = time.perf_counter()

print(count)
print(f'{end-start:.2f}s')

输出:

166231
2.38s

0

我使用了os.walk()

这是我的示例,希望能对你有所帮助

def file_dir():
    directories = []
    res = {}
    cwd = os.getcwd()
    for root, dirs, files in os.walk(cwd):
        for file in files:
            if file.endswith(".tsv"):
                directories.append(os.path.join(root, file))
    res['dir'] = directories
    return res

我不知道为什么 if file.endswith(".tsv"):,但如果我删除这行代码,你的程序就能完美运行。 - crazycat256
这是我的示例。 - Ashkan Goleh Pour

0

你也可以直接使用命令:

find DIR_NAME -type f | wc -l

这将返回所有文件的计数 使用os.system(),可以从Python中完成此操作。


这个命令在哪个操作系统上有效? - crazycat256
这个特定的命令是Linux命令,但在Mac上也应该可以使用。不幸的是,我没有使用Windows的经验,但是你可以在这里查找相应的Windows命令。 - LW42

0

使用库osPath的另一种解决方案:

from pathlib import Path
from os.path import isfile

len([x for x in Path('./dir1').rglob('*') if isfile(x)])

0

正确的方法是使用os.walk,正如其他人所指出的那样,但为了提供另一种尽可能接近您原始代码的解决方案:

您可以使用{{link1:os.scandir}}来避免构建整个列表的成本,这应该会更快:

def get_file_count(directory: str) -> int:
    count = 0

    for entry in os.scandir(directory):
        if entry.is_file():
            count += 1

        elif entry.is_dir():
            count += get_file_count(os.path.join(directory, entry.name))

    return count

0

这里还有另一种方法。

import os
import re
import pandas as pd
 
def count_files(top, pattern, list_files):
  top = os.path.abspath(os.path.expanduser(top))
  res = []
  for root, dirs, files in os.walk(top):
    name_space = os.path.relpath(root, top)
    level = os.path.normpath(name_space).count(os.sep) + 1 if name_space != '.' else 0
    matches = [file for file in files if re.search(pattern, file)]
    if matches:
      if list_files:
        res.append((pattern, level, name_space, len(matches), matches))
      else:
        res.append((pattern, level, name_space, len(matches)))

  if list_files:
    df = pd.DataFrame(res, columns=['pattern', 'level', 'name_space', 'count', 'files'])
  else:
    df = pd.DataFrame(res, columns=['pattern', 'level', 'name_space', 'count'])
  return df

考虑以下目录结构。
rajulocal@hogwarts ~/x/x5 % tree -a 
.
├── analysis.txt
├── count_files.ipynb
├── d1
│   ├── d2
│   │   ├── d3
│   │   │   └── f5.txt
│   │   ├── f3.txt
│   │   └── f4.txt
│   ├── f2.txt
│   └── f6.txt
├── f1.txt
├── f7.txt
└── .ipynb_checkpoints
    └── count_files-checkpoint.ipynb

4 directories, 10 files

统计每个目录中的文本文件数量(即以 .txt 结尾的文件)

rajulocal@hogwarts ~/x/x5 % ipython
Python 3.10.6 (main, Oct 24 2022, 16:07:47) [GCC 11.2.0]
Type 'copyright', 'credits' or 'license' for more information
IPython 8.6.0 -- An enhanced Interactive Python. Type '?' for help.
...
In [2]: 
df = count_files("~/x/x5", "\.txt", False)
df
Out[2]: 
  pattern  level name_space  count
0   \.txt      0          .      3
1   \.txt      1         d1      2
2   \.txt      2      d1/d2      2
3   \.txt      3   d1/d2/d3      1

查看这些文件是什么

In [3]: 
df = count_files("~/x/x5", "\.txt", True)
df
Out[3]: 
  pattern  level name_space  count                           files
0   \.txt      0          .      3  [analysis.txt, f1.txt, f7.txt]
1   \.txt      1         d1      2                [f6.txt, f2.txt]
2   \.txt      2      d1/d2      2                [f4.txt, f3.txt]
3   \.txt      3   d1/d2/d3      1                        [f5.txt]

获取文件总数
In [4]: 
df['count'].sum()
Out[4]: 
8

计算以 .ipynb 结尾的文件数量(ipython 笔记本文件)

In [5]: 
df = count_files("~/x/x5", "\.ipynb", True)
df
Out[5]: 
   pattern  level          name_space  count                           files
0  \.ipynb      0                   .      1             [count_files.ipynb]
1  \.ipynb      1  .ipynb_checkpoints      1  [count_files-checkpoint.ipynb]

In [6]: 
df['count'].sum()
Out[6]: 
2

统计所有文件

In [7]: 
df = count_files("~/x/x5", ".*", False)
df
Out[7]: 
  pattern  level          name_space  count
0      .*      0                   .      4
1      .*      1  .ipynb_checkpoints      1
2      .*      1                  d1      2
3      .*      2               d1/d2      2
4      .*      3            d1/d2/d3      1

In [8]: 
df['count'].sum()
Out[8]: 
10

这与tree命令返回的文件数量相匹配。


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