本文目录导读:

这是一个非常实用的需求,文件名编码问题通常出现在从旧系统(如Windows简体中文GBK)、不同语言系统或不同服务器迁移文件时,导致文件名出现乱码(如Îĵµ.txt 或 文档.txt)。
以下是几种主流的批量修改文件名编码的实用脚本方法,按推荐程度排序:
使用 convmv(Linux/macOS 最推荐)
convmv 是一个专为转换文件名编码设计的工具,非常安全(默认只预览,不修改)。
安装
# Debian/Ubuntu sudo apt install convmv # macOS (Homebrew) brew install convmv # CentOS/RHEL sudo yum install convmv
常用脚本示例
场景1: 从 GBK 转换成 UTF-8 (最常见)
# 先在当前目录递归预览(强烈建议先做这一步) convmv -f gbk -t utf-8 --notest ./* # -f 源编码 -t 目标编码 --notest 执行实际转换(去掉则只预览) # Windows 用户如果遇到带BOM的文件名,可能需要将目标指定为 utf8-bom # convmv -f gbk -t utf-8-bom --notest ./*
场景2: 从 EUC-JP(日文) 转换成 UTF-8
convmv -f eucjp -t utf-8 --notest ./*
场景3: 没有 convmv 时,可以用 iconv 配合 rename
# 适用于少量文件,且需要手动处理循环
for file in *; do
new=$(echo "$file" | iconv -f gbk -t utf-8)
if [ "$file" != "$new" ]; then
mv "$file" "$new"
fi
done
使用 Python 脚本(跨平台通用)
这是最灵活、最强大的方法,可以处理复杂的编码探测和自定义逻辑。
脚本: batch_fix_filename_encoding.py
#!/usr/bin/env python3
import os
import sys
import argparse
def decode_filename(name, src_encoding, target_encoding='utf-8'):
"""
尝试将文件名从 src_encoding 解码(修正乱码),再重编码为 target_encoding
如果文件名本身就包含无效字符,需要先按二进制处理。
"""
try:
# 方法1:假设文件名已经是乱码,尝试用源编码解码它
# 注意:这里需要处理文件名本身的 bytes 形式
name_bytes = name.encode('latin-1') # 先转成 bytes(latin-1 无损耗)
decoded_name = name_bytes.decode(src_encoding)
return decoded_name
except (UnicodeDecodeError, UnicodeEncodeError):
try:
# 方法2:如果文件名本身是正常的 unicode 但字符编码不对
return name.encode('latin-1').decode(src_encoding)
except:
return name # 无法处理则跳过
def batch_convert(root_dir, src_encoding='gbk', target_encoding='utf-8', dry_run=True):
"""
批量递归转换文件名编码
"""
for dirpath, dirnames, filenames in os.walk(root_dir):
# 处理文件
for filename in filenames:
old_path = os.path.join(dirpath, filename)
new_name = decode_filename(filename, src_encoding, target_encoding)
if new_name != filename:
new_path = os.path.join(dirpath, new_name)
if dry_run:
print(f"[试运行] {old_path} -> {new_path}")
else:
try:
os.rename(old_path, new_path)
print(f"[已转换] {old_path} -> {new_path}")
except Exception as e:
print(f"[失败] {old_path}: {e}")
# 处理目录名(需要小心递归顺序,建议先处理文件,再处理目录)
for dirname in dirnames:
old_dirpath = os.path.join(dirpath, dirname)
new_dirname = decode_filename(dirname, src_encoding, target_encoding)
if new_dirname != dirname:
new_dirpath = os.path.join(dirpath, new_dirname)
if dry_run:
print(f"[试运行] 目录 {old_dirpath} -> {new_dirpath}")
else:
try:
os.rename(old_dirpath, new_dirpath)
print(f"[已转换] 目录 {old_dirpath} -> {new_dirpath}")
except Exception as e:
print(f"[失败] 目录 {old_dirpath}: {e}")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='批量修正文件名编码乱码')
parser.add_argument('path', nargs='?', default='.', help='要处理的目录路径(默认当前目录)')
parser.add_argument('-f', '--from', dest='src_encoding', default='gbk', help='原始文件名编码(如gbk、shift_jis、euc-kr)')
parser.add_argument('-t', '--to', dest='target_encoding', default='utf-8', help='目标编码')
parser.add_argument('--force', action='store_true', help='强制执行(默认只预览)')
args = parser.parse_args()
root_dir = args.path
src = args.src_encoding
target = args.target_encoding
dry_run = not args.force
print(f"源编码: {src} -> 目标编码: {target}")
print(f"试运行模式: {dry_run}")
batch_convert(root_dir, src, target, dry_run)
if dry_run:
print("✅ 预览完成,如果无误,请加 --force 参数执行实际转换。")
else:
print("✅ 转换完成!")
用法:
# 预览(推荐) python batch_fix_filename_encoding.py ./乱码目录/ -f gbk # 实际转换 python batch_fix_filename_encoding.py ./乱码目录/ -f gbk --force
PowerShell 脚本(Windows 专属)
Windows 上的文件名通常是 UTF-16(系统内部),但如果你是从 FTP 或旧系统获取的文件,可能会显示为乱码。
PowerShell 脚本:Fix-Encoding.ps1
<#
.SYNOPSIS
批量修复文件名编码(适用于 Windows 下从 FTP 或其他系统下载的乱码文件)
.DESCRIPTION
将乱码文件名从指定编码重新编码为 UTF-8/GBK
#>
param(
[string]$Path = ".",
[string]$FromEncoding = "GBK",
[bool]$DryRun = $true
)
function Convert-FileNameToBytes {
param([string]$name)
# 将当前文件名(可能是乱码)视为某个编码的字节流
# 注意:PowerShell 中文件名实际是 .NET 字符串,需要先取得原始字节
# 这个方法依赖 Windows 行为,可能需要调整
$bytes = [System.Text.Encoding]::GetEncoding(28591).GetBytes($name) # Latin-1
return $bytes
}
function Decode-FileName {
param([string]$name, [string]$fromEnc)
try {
$bytes = Convert-FileNameToBytes $name
$newName = [System.Text.Encoding]::GetEncoding($fromEnc).GetString($bytes)
return $newName
} catch {
return $name
}
}
$items = Get-ChildItem -Path $Path -Recurse
foreach ($item in $items) {
$newName = Decode-FileName $item.Name $FromEncoding
if ($newName -ne $item.Name) {
$newPath = Join-Path $item.DirectoryName $newName
if ($DryRun) {
Write-Host "[预览] $($item.FullName) -> $newPath" -ForegroundColor Yellow
} else {
try {
Rename-Item -Path $item.FullName -NewName $newName
Write-Host "[已转换] $($item.FullName) -> $newPath" -ForegroundColor Green
} catch {
Write-Host "[失败] $($item.FullName) : $_" -ForegroundColor Red
}
}
}
}
if ($DryRun) {
Write-Host "`n✅ 预览完成,若要执行转换,请设置 -DryRun:$false" -ForegroundColor Cyan
}
用法:
# 预览 .\Fix-Encoding.ps1 -Path "D:\乱码目录" -FromEncoding GBK # 执行 .\Fix-Encoding.ps1 -Path "D:\乱码目录" -FromEncoding GBK -DryRun:$false
常见问题与调试技巧
-
如何确定源编码?
- 如果乱码看起来像
Îĵµ.txt,源编码通常是 GBK(或 GB18030)。 - 如果乱码看起来像
文档.txt,源编码通常是 UTF-8(但被系统当成了拉丁文或GBK显示)。 - 如果乱码看起来像 或
锟斤拷,说明多次错误转换,通常需要先转换为 UTF-8,再转换为 GBK 处理。 - 可以使用
chardet(Python库)自动探测:# pip install chardet import chardet with open('文件名样例.txt', 'rb') as f: raw = f.read() result = chardet.detect(raw) print(result['encoding']) # 但 chardet 对文件名效果有限
- 如果乱码看起来像
-
安全第一: 无论是
convmv还是--dry-run,永远先预览再执行。 -
递归与目录名: 如果目录名也是乱码,先处理目录内的文件,再处理目录名(Python 脚本已处理好这个顺序)。
-
特殊字符: 文件名中可能有 、、 等非法字符,如果解码后出现这些字符,需要额外替换:
# 在 decode_filename 函数中添加 new_name = new_name.replace(':', '-').replace('/', '-').replace('\n', '')
对于 Linux/macOS,首选 convmv;对于跨平台或复杂场景,使用 Python 脚本;Windows 用户可以使用 PowerShell 脚本。