【问题标题】:Compare two versions of the same folder and get names of files that differs比较同一文件夹的两个版本并获取不同文件的名称
【发布时间】:2018-10-13 22:25:45
【问题描述】:

假设我有以下简化的文件结构

main_folder
    |__ foo.json
    |
    |__ sub_folder
          |__bar.json

我有两份main_folder,例如main_folder_v1main_folder_v2

我想比较两个版本并获取所有不同文件的名称(例如,获取 "foo.json",以防其内容在 main_folder_v2 中更新)

我使用下面的代码

import filecmp

comparison_result = filecmp.dircmp(main_folder_v1, main_folder_v2)
files_that_differs = comparison_result.diff_files

问题是我会得到["foo.json"],以防它在main_folder_v2 中更新,但我永远不会得到["bar.json"],因为似乎没有执行sub_folder 中的文件比较

是否有可能使用filecmp 递归比较文件夹并获取不同的文件名或os.walk() 是唯一的解决方案?

【问题讨论】:

  • os.walk 有什么问题
  • 我同意@SuperStew 我认为os.walkset.symmetric_difference 会处理这个问题
  • @CoryKramer,SuperStew,os.walk 没有任何问题。我只是在寻找像comparison_result.diff_files 这样简单的解决方案。如果没有这样的解决方案,我将使用os.walk...

标签: python python-3.x file-comparison


【解决方案1】:

[Python]: filecmp - File and Directory Comparisons 支持通过dircmp.subdirs 进行递归遍历。不需要os.walk(或任何其他类似功能)。

code.py

import sys
import filecmp
import os


main_folder_v1 = "dir_v1"
main_folder_v2 = "dir_v2"

ROOT_DIR_MARKER = ""


def traverse_dircmp(dircmp_obj, dir_name=ROOT_DIR_MARKER):
    for item in dircmp_obj.diff_files:
        yield os.path.join(dir_name, item)
    for subdir_name in dircmp_obj.subdirs:
        yield from traverse_dircmp(dircmp_obj.subdirs[subdir_name], dir_name=os.path.join(dir_name, subdir_name))
        #for item in traverse_dircmp(dircmp_obj.subdirs[subdir_name], dir_name=os.path.join(dir_name, subdir_name)):
        #    yield item


def traverse_dircmp_list(dircmp_obj, dir_name=ROOT_DIR_MARKER):
    ret = [os.path.join(dir_name, item) for item in dircmp_obj.diff_files]
    for subdir_name in dircmp_obj.subdirs:
        ret.extend(traverse_dircmp_list(dircmp_obj.subdirs[subdir_name], dir_name=os.path.join(dir_name, subdir_name)))
    return ret


def main():
    comparison_object = filecmp.dircmp(main_folder_v1, main_folder_v2)

    comparison_result = traverse_dircmp(comparison_object)
    print("{:s}: {:}".format("Different files (gen)", list(comparison_result)))

    comparison_result_list = traverse_dircmp_list(comparison_object)
    print("{:s}: {:}".format("Different files (list)", comparison_result_list))


if __name__ == "__main__":
    print("Python {:s} on {:s}\n".format(sys.version, sys.platform))
    main()

输出(类似于你的目录结构):

(py35x64_test) e:\Work\Dev\StackOverflow\q050157870>"e:\Work\Dev\VEnvs\py35x64_test\Scripts\python.exe" code.py
Python 3.5.4 (v3.5.4:3f56838, Aug  8 2017, 02:17:05) [MSC v.1900 64 bit (AMD64)] on win32

Different files (gen): ['foo.json', 'subdir00\\bar.json', 'subdir00\\subdir001\\x.json']
Different files (list): ['foo.json', 'subdir00\\bar.json', 'subdir00\\subdir001\\x.json']

@EDIT0

  • 修改了 traverse_dircmp 函数以返回文件列表,而不是按照其中一个 cmets 中的要求打印文件

@EDIT1

  • 添加了生成器功能(作为个人练习),这是新的(也是首选)样式,并且在大目录的情况下不会消耗内存(!!需要 Python3.3或更高!,或yield from 语句可以替换为下面的 2 条注释(foryield)行)

【讨论】:

  • 这几乎成功了(+1),但是我不需要从每个子文件夹中打印出文件名,而是需要获取所有不同文件名的完整列表。可以修改traverse_dircmp() 以将每个子文件夹中的项目附加到完整列表并返回吗?
  • 我试图在函数外部定义my_list = [],并通过用print("{:s} - {:}".format(dir_name, dircmp_obj.diff_files)) 替换my_list.extend(dircmp_obj.diff_files) 来更新global my_list,但我想这不是最好的主意...:)
  • 它在上次更新之前工作。我现在无法尝试使用生成器的解决方案,但我相信它也会起作用。谢谢
猜你喜欢
  • 1970-01-01
  • 2012-03-17
  • 1970-01-01
  • 2022-08-14
  • 1970-01-01
  • 2015-08-12
  • 1970-01-01
  • 1970-01-01
  • 2012-12-31
相关资源
最近更新 更多