# -*- coding: utf-8 -*-
from pathlib import Path
import shutil
import csv
import re
# ================= 配置区 =================
TXT_FILE = Path(r"d:\line.txt")
SEARCH_DIR = Path(r"d:\1")
TARGET_DIR = Path(r"d:\2")
INCLUDE_SUBFOLDERS = True
OVERWRITE = True
# True:保留源目录结构
# False:所有文件直接放到 TARGET_DIR
KEEP_FOLDER_STRUCTURE = True
# 日志文件
LOG_FILE = TARGET_DIR / "list.csv"
# ================= 操作模式 =================
# 0 = 预览(只打印不操作)
# 1 = 复制
# 2 = 移动
MODE = 1
# ===========================================
# ================= TXT清洗 =================
def clean_text(x: str):
"""去除不可见字符和空白字符"""
if not x:
return ""
x = x.replace("\ufeff", "")
x = x.replace("\u200b", "")
x = x.replace("\u200c", "")
x = x.replace("\u200d", "")
x = re.sub(r"\s+", "", x)
return x.lower()
def read_txt_names(txt_file: Path):
"""
读取TXT中的文件名。
支持:
1
1.mp4
xxx/1.mp4
G:\\xxx\\1.mp4
"""
names = []
with open(txt_file, "r", encoding="utf-8-sig") as f:
for line in f:
line = clean_text(line)
if not line:
continue
# 如果是表格复制出来的内容,取最后一列
if "\t" in line:
parts = [
clean_text(p)
for p in line.split("\t")
if p.strip()
]
if parts:
line = parts[-1]
name = Path(line).name
if name in ["文件名", "完整路径"]:
continue
# 只匹配文件名主体,不匹配扩展名
names.append(Path(name).stem.lower())
return names
# ================= 重名处理 =================
def get_unique_path(path: Path):
"""目标文件已存在时,生成不重名路径"""
if not path.exists():
return path
parent = path.parent
stem = path.stem
suffix = path.suffix
i = 1
while True:
new_path = parent / f"{stem}_{i}{suffix}"
if not new_path.exists():
return new_path
i += 1
# ================= 生成目标路径 =================
def build_target_path(src: Path):
"""
根据 KEEP_FOLDER_STRUCTURE 生成目标路径。
"""
if KEEP_FOLDER_STRUCTURE:
# 获取文件相对于源目录的路径
relative_path = src.relative_to(SEARCH_DIR)
# 在目标目录下重建同样的目录结构
target_file = TARGET_DIR / relative_path
else:
# 不保留目录结构,全部放到目标根目录
target_file = TARGET_DIR / src.name
return target_file
# ================= 文件操作 =================
def process_file(src: Path, dst: Path):
# 创建目标文件所在目录
dst.parent.mkdir(parents=True, exist_ok=True)
if MODE == 0:
print(f"[预览] {src} -> {dst}")
return "预览"
elif MODE == 1:
shutil.copy2(str(src), str(dst))
print(f"[复制] {src} -> {dst}")
return "复制"
elif MODE == 2:
shutil.move(str(src), str(dst))
print(f"[移动] {src} -> {dst}")
return "移动"
else:
raise ValueError("MODE 必须是 0、1 或 2")
# ================= 主流程 =================
def main():
mode_map = {
0: "预览",
1: "复制",
2: "移动",
}
print(f"当前模式:{MODE}({mode_map.get(MODE)})")
print(f"保留目录结构:{KEEP_FOLDER_STRUCTURE}")
print(f"包含子目录:{INCLUDE_SUBFOLDERS}")
print(f"允许覆盖:{OVERWRITE}")
print("-" * 80)
if not TXT_FILE.exists():
print(f"TXT不存在:{TXT_FILE}")
return
if not SEARCH_DIR.exists():
print(f"源目录不存在:{SEARCH_DIR}")
return
TARGET_DIR.mkdir(parents=True, exist_ok=True)
txt_names = read_txt_names(TXT_FILE)
txt_set = set(txt_names)
print(f"TXT中读取到:{len(txt_names)} 个文件名")
print(f"去重后数量:{len(txt_set)}")
if INCLUDE_SUBFOLDERS:
all_files = [
p
for p in SEARCH_DIR.rglob("*")
if p.is_file()
]
else:
all_files = [
p
for p in SEARCH_DIR.iterdir()
if p.is_file()
]
print(f"源目录中找到文件数量:{len(all_files)}")
print("-" * 80)
success = 0
fail = 0
matched_names = set()
log_rows = []
for file in all_files:
match_key = file.stem.lower()
if match_key not in txt_set:
continue
target_file = build_target_path(file)
if target_file.exists():
if OVERWRITE:
# 直接使用原路径,复制或移动时覆盖
pass
else:
# 不覆盖时自动添加序号
target_file = get_unique_path(target_file)
try:
action = process_file(file, target_file)
matched_names.add(match_key)
if MODE == 0:
log_type = "预览"
else:
success += 1
log_type = "成功"
log_rows.append([
log_type,
match_key,
str(file),
str(target_file),
action,
])
except Exception as e:
fail += 1
print(f"[错误] {file} -> {e}")
log_rows.append([
"失败",
match_key,
str(file),
str(target_file),
str(e),
])
not_found = txt_set - matched_names
for name in sorted(not_found):
log_rows.append([
"未找到",
name,
"",
"",
"源目录中未匹配到",
])
with open(
LOG_FILE,
"w",
newline="",
encoding="utf-8-sig",
) as f:
writer = csv.writer(f)
writer.writerow([
"状态",
"匹配文件名",
"原始路径",
"目标路径",
"备注",
])
writer.writerows(log_rows)
print("-" * 80)
print("全部完成")
print(f"成功:{success}")
print(f"失败:{fail}")
print(f"未找到:{len(not_found)}")
print(f"日志:{LOG_FILE}")
if __name__ == "__main__":
main()
❤️ 转载文章请注明出处,谢谢!❤️