仅对文件/文件夹名称比较目录,输出差异?

2 投票
4 回答
8538 浏览
提问于 2025-04-17 17:03

我想知道怎么递归地比较两个文件夹(比较只基于文件名),并打印出只在一个文件夹里存在的文件或文件夹。

我现在使用的是Python 3.3。

我看到过filecmp这个模块,但它似乎不能完全满足我的需求。最重要的是,它比较文件时考虑的不仅仅是文件名。

这是我目前的代码:

import filecmp
dcmp = filecmp.dircmp('./dir1', './dir2')
dcmp.report_full_closure()

dir1的内容是这样的:

dir1
  - atextfile.txt
  - anotherfile.xml
  - afolder
    - testscript.py
  - anotherfolder
    - file.txt
  - athirdfolder

dir2的内容是这样的:

dir2
  - atextfile.txt
  - afolder
    - testscript.py
  - anotherfolder
    - file.txt
    - file2.txt

我希望结果看起来像这样:

files/folders only in dir1
  * anotherfile.xml
  * athirdfolder

files/folders only in dir2
  * anotherfolder/file2.txt

我需要一个简单的Python方法,只根据文件或文件夹的名称来比较两个目录,并打印出它们的不同之处。

另外,我还需要一种方法来检查这两个目录是否完全相同。

注意:我在StackOverflow和谷歌上搜索过类似的内容。我看到很多例子是比较文件内容的,但我找不到只比较文件名的例子。

4 个回答

1

其实,filecmp这个工具可以用来做这个事情,而且应该用,但你需要写一点代码。

  • 你需要给filecmp.dircmp()两个文件夹,一个叫左边,一个叫右边。
  • filecmp.dircmp.left_only会列出只在左边文件夹里的文件和文件夹。
  • filecmp.dircmp.right_only会列出只在右边文件夹里的文件和文件夹。
  • filecmp.dircmp.common_dirs会列出两个文件夹都有的文件夹。

你可以用这些信息来写一个简单的递归函数,找出两个文件夹中不相同的文件和文件夹。

代码:

from os.path import join
from filecmp import dircmp

def find_uncommon(L_dir, R_dir):
    dcmp = dircmp(L_dir, R_dir)
    L_only = [join(L_dir, f) for f in dcmp.left_only]
    R_only = [join(R_dir, f) for f in dcmp.right_only]
    for sub_dir in dcmp.common_dirs:
        new_L, new_R = find_uncommon(join(L_dir, sub_dir), join(R_dir, sub_dir))
        L_only.extend(new_L)
        R_only.extend(new_R)
    return L_only, R_only

测试案例:

C:/
    L_dir/
        file_in_both_trees.txt
        file_in_L_tree.txt
        dir_in_L_tree/
        dir_in_both_trees/
            file_in_both_trees.txt
            file_in_L_tree.txt
            dir_in_L_tree/
                file_inside_dir_only_in_L_tree.txt
    R_dir/
        file_in_both_trees.txt
        file_in_R_tree.txt
        dir_in_R_tree/
        dir_in_both_trees/
            file_in_both_trees.txt
            file_in_R_tree.txt
            dir_in_R_tree/
                file_inside_dir_only_in_R_tree.txt

演示:

L_only, R_only = find_uncommon('C:\\L_dir', 'C:\\R_dir')
print('Left only:\n\t' + '\n\t'.join(L_only))
print('Right only:\n\t' + '\n\t'.join(R_only))

结果:

Left_only:
    C:\L_dir\file_in_L_tree.txt
    C:\L_dir\dir_in_L_tree
    C:\L_dir\dir_in_both_trees\file_in_L_tree.txt
    C:\L_dir\dir_in_both_trees\dir_in_L_tree
Right_only:
    C:\R_dir\file_in_R_tree.txt
    C:\L_dir\dir_in_R_tree
    C:\R_dir\dir_in_both_trees\file_in_R_tree.txt
    C:\R_dir\dir_in_both_trees\dir_in_R_tree

注意,如果你想查看那些不常见的文件夹里的内容,你需要稍微修改一下上面的代码。我说的就是我上面例子中的这两个文件:

file_inside_dir_only_in_L_tree.txt
file_inside_dir_only_in_R_tree.txt
1

基本的想法是,使用os.walk这个方法来填充文件名的字典,然后对比这些字典。

import os
from os.path import join
fpa = {}
for root, dirs, files in os.walk('/your/path'):
   for name in files:
   fpa[name] = 1

fpb = {}
for root, dirs, files in os.walk('/your/path2'):
   for name in files:
   fpb[name] = 1

print "files only in a"
for name in fpa.keys():
    if not(name in fpb.keys()):
        print name,"\n"

print "files only in b"
for name in fpb.keys():
    if not(name in fpa.keys()):
        print name,"\n"

我没有测试过这个,所以你可能需要自己修正一下。另外,这个方法也可以很容易地进行改进,以避免重复使用。

7

我的解决方案使用了set()这种类型来存储相对路径。然后比较的时候只需要进行集合的减法运算。

import os
import re

def build_files_set(rootdir):
    root_to_subtract = re.compile(r'^.*?' + rootdir + r'[\\/]{0,1}')

    files_set = set()
    for (dirpath, dirnames, filenames) in os.walk(rootdir):
        for filename in filenames + dirnames:
            full_path = os.path.join(dirpath, filename)
            relative_path = root_to_subtract.sub('', full_path, count=1)
            files_set.add(relative_path)

    return files_set

def compare_directories(dir1, dir2):
    files_set1 = build_files_set(dir1)
    files_set2 = build_files_set(dir2)
    return (files_set1 - files_set2, files_set2 - files_set1)

if __name__ == '__main__':
    dir1 = 'old'
    dir2 = 'new'
    in_dir1, in_dir2 = compare_directories(dir1, dir2)

    print '\nFiles only in {}:'.format(dir1)
    for relative_path in in_dir1:
        print '* {0}'.format(relative_path)

    print '\nFiles only in {}:'.format(dir2)
    for relative_path in in_dir2:
        print '* {0}'.format(relative_path)

讨论

  • 主要的功能是build_files_set()这个函数。它会遍历一个目录,并创建一个包含相对文件和目录名称的集合。

  • compare_directories()这个函数接收两个文件集合,并返回它们之间的差异——非常简单明了。

撰写回答