Python fileinput遇异常时如何保持原文件内容不变?
问题:fileinput原地替换触发UnicodeDecodeError导致原文件被清空,如何保留原文件?
我用Python 3的fileinput模块做文件原地文本替换,部分非可读/二进制文件会触发UnicodeDecodeError,异常发生时原文件内容会被清空——原文件内容会被移至.bak文件,但异常后.bak文件被删除且没恢复到原文件。我没法提前预判所有触发异常的文件类型,没法用if语句排除这些文件,请问怎么在异常发生时不对文件做任何操作,保持原文件内容不变?
原代码示例:
for subdir, dirs, files in os.walk(currWorkingDir): for file in files: try: for line in fileinput.input(os.path.join(subdir, file), inplace=True, encoding='utf-8'): line = line.replace('XXX', 'x') print(line, end='') except UnicodeDecodeError as e: sys.stderr.write('binary file and should not be modified {}.\n'.format( os.path.join(subdir, file))) fileinput.close()
解决方案
核心问题是fileinput开启inplace=True后,会先把原文件重命名为.bak,再创建新的同名文件写入内容。一旦解码失败抛出异常,程序中断时新文件是空的,且默认情况下.bak会被自动清理,导致原文件丢失。下面提供两种可行方案:
方案1:预校验文件编码,提前跳过无法解码的文件
在执行替换操作前,先尝试用指定编码读取文件(只读前几行即可,提升效率),确认能正常解码后再进入fileinput流程,否则直接跳过。
修改后的代码:
import os import sys import fileinput currWorkingDir = "./your_target_dir" # 替换为你的目标目录 for subdir, dirs, files in os.walk(currWorkingDir): for file in files: file_path = os.path.join(subdir, file) # 预校验文件是否能以utf-8解码 try: with open(file_path, 'r', encoding='utf-8') as f: # 只读前10行做校验,避免读取大文件耗时 for _ in range(10): next(f) except UnicodeDecodeError: sys.stderr.write(f"二进制文件,跳过修改:{file_path}\n") continue # 校验通过,执行替换 try: for line in fileinput.input(file_path, inplace=True, encoding='utf-8'): line = line.replace('XXX', 'x') print(line, end='') except Exception as e: sys.stderr.write(f"处理文件出错:{file_path},错误信息:{str(e)}\n")
方案2:捕获异常后手动恢复备份文件
如果必须在fileinput执行过程中处理异常,可以指定backup参数保留备份文件,一旦触发异常,就把备份文件恢复回原文件。
修改后的代码:
import os import sys import fileinput import shutil currWorkingDir = "./your_target_dir" # 替换为你的目标目录 for subdir, dirs, files in os.walk(currWorkingDir): for file in files: file_path = os.path.join(subdir, file) backup_path = f"{file_path}.bak" try: # 指定backup参数,强制保留备份文件 for line in fileinput.input(file_path, inplace=True, encoding='utf-8', backup='.bak'): line = line.replace('XXX', 'x') print(line, end='') # 处理成功后删除备份文件(可选操作) if os.path.exists(backup_path): os.remove(backup_path) except UnicodeDecodeError: sys.stderr.write(f"二进制文件,恢复原文件:{file_path}\n") # 将备份文件恢复为原文件 if os.path.exists(backup_path): shutil.move(backup_path, file_path) except Exception as e: sys.stderr.write(f"处理文件出错,恢复原文件:{file_path},错误信息:{str(e)}\n") if os.path.exists(backup_path): shutil.move(backup_path, file_path)
注意点
- 方案1效率更高,能提前过滤掉大部分无法解码的文件,避免进入修改流程;
- 方案2的
backup参数会强制生成备份文件,处理成功后记得手动删除(如果不需要保留的话); - 极端情况下,文件前几行是文本但后续是二进制,方案1可能漏判,此时可以结合两种方案,在
fileinput循环内也捕获异常并恢复备份。
内容的提问来源于stack exchange,提问作者John Wicked
相关产品推荐
相关产品推荐

