目录
一、为啥我折腾好几天整个这么个没啥用的脚本?
-
如题,我整了个移动文件的python脚本,就…很无语🙄🙄。
-
我编程的习惯还不错,写文件还算能分目录写,但我脑子抽了,寻思着优化一下文件结构,然后喜滋滋的整了个python脚本,当时也没多想,一是寻思着胡乱写的脚本应该跑不起来,二是好奇Python脚本是怎么移动文件的。当时竟然没备份文件,结果是Python脚本居然跑起来了,但明显逻辑有问题——我根目录下的文件全跑到一个文件夹里去了。
-
但我这情况确实特殊,当时学
cmd
命令行,用dir \b \s
得到的文件夹的文件路径我居然脑抽风保存过,虽然是半个月前的,但总比没有好。 -
但我寻思着我也不能一个文件一个文件移动啊,干脆折腾出了一个Python脚本移动文件,我寻思着这个脚本一辈子也就这一次了。
二、这个Python脚本长啥样?
如下模样:
import os
import shutil
import difflib
def get_most_similar_filename(filename, target_filenames):
return difflib.get_close_matches(filename, target_filenames, n=1, cutoff=0.7)
def move_files(original_dir, target_dir):
original_filenames = os.listdir(original_dir)
target_filenames = os.listdir(target_dir)
not_moved_count = 0
if not os.path.exists(target_dir):
os.makedirs(target_dir)
for filename in original_filenames:
source_file = os.path.join(original_dir, filename)
if os.path.isfile(source_file):
similar_filename = get_most_similar_filename(filename, target_filenames)
if similar_filename:
target_file = os.path.join(target_dir, similar_filename[0])
try:
shutil.move(source_file, target_file)
print(f"Moved: {filename} -> {similar_filename[0]}")
except Exception as e:
print(f"Error moving {filename} to {target_file}: {e}")
not_moved_count += 1
else:
print(f"Warning: Target path not found for filename: {filename}")
not_moved_count += 1
else:
print(f"Warning: Source path not found for filename: {filename}")
not_moved_count += 1
return not_moved_count
def main():
original_dir = "D:\\A_Shun_Exclusive\\life_ofuniversity\\code_collection\\copy_vscode_copy\\VSCODE\\"
target_dir = "D:\\A_Shun_Exclusive\\life_ofuniversity\\code_collection\\C\\VSCODE\\"
print("Moving files...")
not_moved_count = move_files(original_dir, target_dir)
print(f"Total files not moved: {not_moved_count}")
if __name__ == "__main__":
main()
三、这个脚本怎么用?
- 将脚本中的
original_dir
设置为源文件夹的路径,将target_dir
设置为目标文件夹的路径。
original_dir = "D:\\A_Shun_Exclusive\\life_ofuniversity\\code_collection\\copy_vscode_copy\\VSCODE\\"
target_dir = "D:\\A_Shun_Exclusive\\life_ofuniversity\\code_collection\\C\\VSCODE\\"
- 你需要在此脚本文件的同级目录下新建两个文件并存放目录
注意: 两个文件的命名必须是original_paths.txt
(存放原始路径)和target_paths.txt
(目标路径)
- 然后运行
python
文件即可
四、基于此Python脚本作以下简单讲述
这个Python脚本旨在解决一个实际问题:如何将一个文件夹中的文件按照它们的内容移动到另一个文件夹中,并确保它们与目标文件夹中的现有文件有足够的相似性以进行匹配。
以下是脚本的简要讲述:
-
获取文件名列表:
- 从源文件夹 (
original_dir
) 中获取所有文件名。 - 从目标文件夹 (
target_dir
) 中获取所有文件名。
- 从源文件夹 (
-
文件移动核心逻辑:
- 对于源文件夹中的每个文件:
- 首先,检查文件是否存在于源文件夹中,如果不存在,输出警告信息。
- 然后,使用
difflib.get_close_matches
函数找到在目标文件夹中最相似的文件名。 - 如果找到了相似的文件名,将源文件移动到目标文件夹中相应的位置,同时输出移动的文件名和目标文件名。
- 如果没有找到相似的文件名,输出警告信息。
- 对于源文件夹中的每个文件:
-
定制相似性阈值:
- 你可以根据需要调整相似性的阈值,通过修改
get_most_similar_filename
函数中的cutoff
参数。默认值是0.7
,你可以根据实际情况将其调整为更高或更低的值,以控制匹配的严格程度。
- 你可以根据需要调整相似性的阈值,通过修改
-
运行脚本:
- 在脚本中设置源文件夹 (
original_dir
) 和目标文件夹 (target_dir
) 的路径。 - 运行脚本,它将自动执行文件的移动操作。
- 在脚本中设置源文件夹 (
-
输出信息:
- 脚本会输出每个文件的移动结果,包括已移动的文件名和目标文件名。
- 如果某些文件无法移动,会输出错误信息和警告信息,同时统计未移动的文件数量。
五、我是如何使用Python脚本语言实现文件移动功能的?
import os
import shutil
import difflib
- 这里我们导入了三个Python库:
os
用于处理文件和目录操作,shutil
用于文件操作(如移动文件),difflib
用于字符串比较(用于找到相似的文件名)。
def get_most_similar_filename(filename, target_filenames):
return difflib.get_close_matches(filename, target_filenames, n=1, cutoff=0.7)
get_most_similar_filename
是一个自定义函数,它的目的是找到与给定文件名(filename
)最相似的目标文件名。target_filenames
参数是目标文件名的列表。- 我们使用了
difflib.get_close_matches
函数,它接受四个参数:filename
:要比较的字符串。target_filenames
:目标字符串列表。n
:要返回的最佳匹配数量(在此处为1)。cutoff
:相似性的阈值,0.7 表示只返回相似度大于等于0.7的匹配项。
def move_files(original_dir, target_dir):
original_filenames = os.listdir(original_dir)
target_filenames = os.listdir(target_dir)
not_moved_count = 0
move_files
是另一个自定义函数,它接受两个参数:original_dir
(源文件夹路径)和target_dir
(目标文件夹路径)。- 使用
os.listdir
函数从源文件夹和目标文件夹中获取文件名列表,并将它们存储在original_filenames
和target_filenames
中。 not_moved_count
是一个计数器,用于跟踪未移动的文件数量。
if not os.path.exists(target_dir):
os.makedirs(target_dir)
- 这个条件检查目标文件夹是否存在。如果不存在,它将使用
os.makedirs
创建目标文件夹。
for filename in original_filenames:
source_file = os.path.join(original_dir, filename)
if os.path.isfile(source_file):
- 这是一个循环,它遍历
original_filenames
中的每个文件名。 - 使用
os.path.join
函数创建源文件的完整路径,并将其存储在source_file
变量中。 - 使用
os.path.isfile
函数检查source_file
是否是一个文件,而不是目录。
similar_filename = get_most_similar_filename(filename, target_filenames)
- 这一行代码的目的是使用
get_most_similar_filename
函数找到与源文件名 (filename
) 最相似的目标文件名。 get_most_similar_filename
函数被调用,它接受两个参数:源文件名 (filename
) 和目标文件名列表 (target_filenames
)。- 函数返回的结果存储在
similar_filename
变量中,这是一个列表,包含了与源文件名最相似的一个或多个目标文件名。
if similar_filename:
- 这是一个条件语句,用于检查是否找到了与源文件名相似的目标文件名。如果
similar_filename
列表非空(意味着找到了相似的文件名),则执行以下代码块。
target_file = os.path.join(target_dir, similar_filename[0])
- 这一行代码的目的是创建目标文件的完整路径,以便后续将源文件移动到目标位置。
- 使用
os.path.join
函数将target_dir
和similar_filename[0]
(最相似的目标文件名)连接起来,生成目标文件的路径,并将其存储在target_file
变量中。
try:
shutil.move(source_file, target_file)
print(f"Moved: {filename} -> {similar_filename[0]}")
- 这里使用了
try
和except
块,用于尝试执行文件移动操作。 shutil.move
函数用于将源文件 (source_file
) 移动到目标文件 (target_file
) 的位置。- 如果移动操作成功,那么会执行
print
语句,输出一条消息,显示已移动的文件名以及目标文件名。
except Exception as e:
print(f"Error moving {filename} to {target_file}: {e}")
not_moved_count += 1
- 如果在移动文件时发生了任何错误,将进入
except
块。 - 在
except
块中,我们捕获了异常,并将其存储在e
变量中。异常是一种表示错误的特殊情况。 - 然后,我们使用
print
语句输出一条错误消息,其中包含了文件移动失败的详细信息,包括文件名和目标文件名。 - 同时,我们增加
not_moved_count
计数器的值,以跟踪未能移动的文件数量。
else:
print(f"Warning: Target path not found for filename: {filename}")
not_moved_count += 1
else:
print(f"Warning: Source path not found for filename: {filename}")
not_moved_count += 1
- 如果找不到相似的文件名,我们会输出警告消息,指出目标文件路径或源文件路径未找到,并增加
not_moved_count
的计数器。
return not_moved_count
- 最后,
move_files
函数返回未移动的文件数量,以便在主程序中显示。
def main():
original_dir = "D:\\A_Shun_Exclusive\\life_ofuniversity\\code_collection\\copy_vscode_copy\\VSCODE\\"
target_dir = "D:\\A_Shun_Exclusive\\life_ofuniversity\\code_collection\\C\\VSCODE\\"
main
函数是脚本的主要入口点。- 在函数内部,定义了两个变量
original_dir
和target_dir
,它们分别存储了源文件夹路径和目标文件夹路径。这些路径是硬编码在脚本中的,通常在实际应用中,用户可以通过命令行参数或配置文件来指定这些路径,以增加灵活性。
print("Moving files...")
- 这一行代码用于在控制台输出一条信息,指示脚本正在移动文件。
not_moved_count = move_files(original_dir, target_dir)
- 这一行代码调用了之前定义的
move_files
函数,将original_dir
和target_dir
作为参数传递给该函数。 - 调用
move_files
函数会执行文件移动操作,并返回未移动的文件数量,这个数量将存储在not_moved_count
变量中。
print(f"Total files not moved: {not_moved_count}")
- 最后,这一行代码输出了未移动的文件数量,以供用户查看。
- 使用
f-string
(格式化字符串),将not_moved_count
的值插入到输出消息中。
if __name__ == "__main__":
main()
- 这是一个条件语句,检查脚本是否直接运行,而不是被其他脚本导入。
- 如果脚本直接运行(即
__name__
变量的值为"__main__"
),则调用main
函数,开始执行文件移动操作。
六、这个脚本还有哪些缺陷?
-
相似性匹配不够精确:脚本使用了
difflib.get_close_matches
来寻找最相似的文件名,但它仅基于字符串相似度来匹配文件,可能会出现误匹配的情况,特别是在文件名之间存在较大差异时。对于更高精度的匹配,可以考虑使用更复杂的算法,如文本相似性度量。 -
异常处理不够健壮:脚本在文件移动时使用了
try
和except
块来处理异常,但它捕获了所有异常,包括可能的操作系统或文件系统错误。更好的做法是针对特定类型的异常进行处理,以便更好地了解出现的问题,并采取相应的措施。 -
没有回滚机制:如果在文件移动期间发生错误,脚本不会执行回滚操作,而是继续移动下一个文件。这可能导致一些文件被移动,而其他文件未能成功移动。在实际应用中,添加回滚机制,将确保文件操作的原子性。
-
不提供用户友好的界面:脚本的配置和使用需要直接编辑脚本文件,对于非技术用户可能不够友好。
-
没有错误日志记录:虽然脚本会输出错误消息,但它没有记录这些错误,因此用户无法轻松地回顾或分析错误历史。添加错误日志记录功能可以帮助用户更好地管理问题。
-
不处理文件冲突:如果目标文件夹中已经存在与源文件夹中的文件相同的文件名,脚本将直接覆盖目标文件。在一些情况下,可能需要添加处理文件冲突的逻辑,以避免数据丢失。
-
缺乏单元测试和文档:脚本缺乏单元测试和详细文档,这使得难以验证其功能是否正常工作,并且难以为其他人提供使用说明。添加适当的测试用例和文档可以提高脚本的可维护性和可用性。
简单聊聊Python
不可否认的是,Python应用的确广泛。任一领域的研究一旦涉及编程,Python都是不错的选择;青少年大多都会接触Python而非其他语言。
为什么?
因为Python足够简单!
-
Python之禅:Python有一篇著名的文档叫做《Python之禅》(The Zen of Python),其中包含了Python的设计原则和哲学。这篇文档以诗意的方式表达了Python的简洁、可读性和优雅之美。例如,其中的一句是:“优美胜于丑陋”(Beautiful is better than ugly)。
-
简单明了的语法:Python足够简洁和直观。它强调可读性,使得代码看起来就像阅读英文一样,非常容易理解。
-
交互式解释器:Python的交互式解释器允许你逐行执行代码。
结束语
折腾出这个Python脚本的过程异常痛苦,我常常陷入改错、百度、懵逼的循环中。所以当我实现了我想要的功能时,难以言喻的兴奋感简直让我沉浸其中难以自拔。几乎可以说,我很少有过如此失态的经历。
Python的简洁、优雅让我大吃一惊,我自认使用不管是C语言还是Java语言实现移动文件的功能或许都是一个不小的工程量,我甚至觉得无从下手。
不管前景如何,Python足够有趣,仅此一点,我推荐你们去了解Python。