Python文件操作全攻略:从读写到打包exe的完整实践指南

发布时间:2026/9/10 3:26:43
Python文件操作全攻略:从读写到打包exe的完整实践指南 从“python 文件”这个标题说起它其实比字面上要宽泛得多。我见过太多人第一次接触Python第一个动作就是建一个.py文件然后开始写print(hello)也有不少人卡在“文件读不进去”“Excel导出只有65536行”“打包exe失败”这类跟文件打交道的问题上。这篇文章就围绕Python文件操作的方方面面从最基础的文件读写到环境变量、打包发布再到真实场景里的批处理脚本把我踩过的坑和验证过的方案一次说清楚。适合刚入门的新手也适合已经在写脚本但总被文件编码、路径坑到的人。1. 内容整体设计与思路拆解1.1 为什么文件操作是Python的第一道门槛Python这两年能火遍各个领域一个重要原因就是它处理文件特别顺手。爬虫爬下来的数据要写进文件数据分析要读CSV、Excel运维要批量重命名日志开发要解析配置文件甚至打包成exe可执行文件本质上也是在处理文件。可以说Python跟文件的关系就像切菜刀跟厨师的关系——你绕不开而且越早掌握后面越省心。我见过很多初学者一开始觉得文件操作不就是open()然后read()嘛结果一写就崩。编码不对、路径写错、文件被占用、读完没关闭、跨平台换行符不一样……这些问题单独看都不难但串在一起就特别打击信心。所以我这篇内容的整体思路是先把最核心的读写模型讲透再往文件形态的上下游延伸——生成文件的数据处理、运行文件的工程环境、打包文件的可执行交付最后用一套实战脚本收尾。1.2 方案选型用标准库还是第三方库很多教程一上来就叫人装pandas我反倒觉得没必要。处理简单文本、配置文件、批量复制重命名Python标准库就够了os、shutil、glob、csv、json这些模块都是自带不需要装任何东西。等数据量大了、要处理Excel样式了再上openpyxl、pandas这类第三方库这才是合理的演进路线。选型逻辑很简单文件操作的第一原则是“你的项目需要跑多久、跑在哪里”。如果你只想把日志里的关键字抽出来用open()配合循环就完事了。要是你需要对几万行的Excel做汇总透视再考虑pandas。如果一上来就引一堆依赖回头打包exe的时候文件体积直接奔着150MB去了那才是给自己找麻烦。1.3 从文件到工程一条完整的学习路径我给新手的建议是不要只盯着“读写”两个字。一个完整的Python文件项目通常要经历这四个阶段阶段一读写单文件搞清楚路径、编码、关闭。阶段二批量操作多文件用glob和shutil搞定自动化。阶段三处理结构化数据文件比如CSV、Excel、JSON。阶段四把脚本变成一个环境独立、可执行、可交付的产物也就是虚拟环境和exe打包。这篇文章的结构就是按这条路来的。每一步我都会把“为什么这么干”讲清楚不讲废话。2. 核心细节解析与实操要点2.1 路径处理反斜杠、绝对路径和相对路径的坑先解决一个最基础但最容易翻车的问题路径。Windows上路径长这样C:\Users\me\data\file.txt。但Python字符串里\是转义符所以直接写open(C:\Users\me\data.txt)大概率报错因为\U被当成Unicode转义。解决办法有三个层次用原始字符串rC:\Users\me\data.txt把反斜杠换成斜杠C:/Users/me/data.txtWindows和Linux都能识别用pathlib模块我推荐这个from pathlib import Path data_dir Path(rC:/Users/me/data) file_path data_dir / file.txt # 用 / 拼接跨平台安全为什么推荐pathlib因为它把路径当成对象处理exists()、read_text()、write_text()都是方法不用反复去import os再拼字符串。Python 3.6以后自带完全够用。还有个概念必须分清楚当前工作目录。脚本所在的目录不一定是运行命令时的目录。你在命令行敲python C:\project\run.py但当前目录是C:\project外的某个位置Python的相对路径就是基于那个“当前位置”算的不是基于脚本位置。如果你不确定最好在脚本开头加上import os os.chdir(os.path.dirname(os.path.abspath(__file__)))这句会把当前工作目录切到脚本所在目录。当然如果你的脚本将来要被别人用包管理器安装执行可能更希望用绝对的pathlib逻辑。但开发阶段这个习惯能救你很多次。2.2 文本文件读写用with而不是open后close我们最常用的读文件方式很多老手也会写f open(data.txt, r, encodingutf-8) content f.read() f.close()这样写有个隐患如果f.read()中间抛了异常close()就不会执行文件句柄一直被占着。在Windows上文件可能被锁定后续你想删除或重命名它就报“另一个程序正在使用此文件”。正确姿势是上下文管理器with open(data.txt, r, encodingutf-8) as f: content f.read() # 无论有没有异常with块结束后文件都会自动关闭在with块里你也可以一行一行读with open(access.log, r, encodingutf-8) as f: for line in f: print(line.strip())文件对象本身是迭代器直接for line in f不会一次性把整个文件加载进内存适合处理大日志文件。这是新手最容易忽略的点read()是全文读入readlines()也是如果文件几个GB内存直接爆掉。写文件同样要用with并且注意模式w覆盖写文件不存在会创建a追加写文件指针在末尾x独占创建文件已存在会报错可读可写比如r一个小小的实用技巧如果你不确定文件是否存在用x模式可以安全地避免覆盖已有文件。2.3 编码问题utf-8不是唯一的答案Python 3里open()默认编码是locale.getpreferredencoding()在Windows中文系统上通常是gbk。这就产生一个经典报错UnicodeDecodeError: gbk codec cant decode byte 0x... in position ...: illegal multibyte sequence原因就是文件本身是utf-8编码但你用gbk去解码。所以我的建议是任何文件读写都显式指定encodingutf-8并且保证文件本身真是utf-8。如果你接手的是老系统导出的GBK文件那就反过来用encodinggbk或者更保险一点with open(data.csv, r, encodinggbk, errorsignore) as f: content f.read()errorsignore会静默忽略非法字节代价是可能丢内容所以只适合你想先看看情况的时候。真正要处理多来源文件用chardet或charset-normalizer库自动检测编码pip install charset-normalizerfrom charset_normalizer import from_path best from_path(data.csv).best() print(best.encoding)编码是文件操作里最隐蔽的坑尤其当你把代码从一个平台拉到另一个平台比如Windows写的脚本放到Linux服务器上跑必须显式指定不能指望系统默认。2.4 二进制文件图片、压缩包和专用格式文本文件之外还有一大类二进制文件。读取二进制要用rb模式写用wb。二进制文件不能按行读通常是一次性读入或分段读取with open(image.png, rb) as f: data f.read() # bytes对象复制二进制文件最稳妥的方式是分块复制避免大文件全载入内存with open(source.zip, rb) as src, open(target.zip, wb) as dst: while chunk : src.read(4096): dst.write(chunk)这里用到了海象运算符:意思是“读取一块如果非空就写出去”。4096字节是经典的缓冲区大小你也可以用shutil.copyfile()一行搞定标准库已经帮你封装好了import shutil shutil.copyfile(source.zip, target.zip)如果只是复制文件shutil永远是首选不要自己手写循环。只有需要读取文件内容做二次处理比如给图片加缩放、解析压缩包里的某些字节时才用二进制读写。3. 实操过程与核心环节实现3.1 安装Python与环境变量跨平台的实际操作网上搜索“python安装教程”的热度一直不减很多问题其实不是Python本身的问题而是环境变量没配好。Windows安装Python时第一步就有一个勾选项“Add Python to PATH”这个一定要勾上。如果忘了勾安装完成后python命令会提示“不是内部或外部命令”。这时候可以手动配右键“此电脑” → 属性 → 高级系统设置 → 环境变量在“系统变量”的Path里加入Python安装目录和它的Scripts子目录。安装目录通常是C:\Users\你的用户名\AppData\Local\Programs\Python\Python311\和同一路径下的Scripts\。在Linux或者macOS上一般系统自带Python但咱自己开发建议用python3命令来区分系统其他脚本。如果你需要管理多个Python版本推荐用pyenvLinux/macOS或Windows上的pylauncher。在Windows里py -3.10可以指定使用3.10版本解释器。装完Python紧接着要会创建虚拟环境。为什么要虚拟环境因为你不同的项目可能依赖不同版本的三方库比如项目A需要pandas 1.5项目B需要pandas 2.0装在一起就打架。虚拟环境就是给每个项目单独开一个“文件柜”。# Windows python -m venv venv venv\Scripts\activate # Linux/macOS python3 -m venv venv source venv/bin/activate激活后命令提示符会多一个(venv)前缀这时候你pip install的东西都只在这个环境里生效。退出用deactivate。这里有个高频搜索词“npm : 无法加载文件 ... npm.ps1因为在此系统上禁止运行脚本”其实跟Python关系不大但经常在同一个终端里出现。这是PowerShell的执行策略限制不是npm或Python坏了。解决办法有两种当前会话临时放开Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process永久放开注意安全管理员身份执行Set-ExecutionPolicy RemoteSigned后面这个策略的意思是本机脚本可以运行从网络下载的脚本需要签名。国内很多用Windows做开发的都被这个坑过知道原因后其实不难。3.2 VSCode配置Python环境的正确姿势VSCode是目前最流行的Python编辑器但很多人装完插件却没法跑核心问题还是环境没选对。你需要做几步安装官方Python扩展微软出的那个。按CtrlShiftP输入“Python: Select Interpreter”选择你虚拟环境里的python.exe。配置好.vscode/launch.json一般第一次按F5运行时会提示你选环境选“Python File”就行。我见过很多同学在VSCode里写代码写得好好的一运行就报“ModuleNotFoundError: No module named pandas”。原因往往是VSCode里右下角选择的解释器是全局Python不是你的虚拟环境。而你在终端里明明pip install pandas成功。解决办法就是确认解释器路径和环境一致。还有一个很冷门但很实用的设置VSCode的集成终端默认使用PowerShell如果执行策略禁止脚本那么你激活虚拟环境的activate.ps1也会报错。你可以把VSCode默认终端切换成Command Prompt或者干脆按上面说的设置执行策略。切换方法CtrlShiftP→ “Terminal: Select Default Profile” → 选“Command Prompt”。3.3 用PyInstaller把Python脚本打包成exe文件“python转exe文件”也是搜索热词。很多人写了个小工具想发给没有Python环境的同事用就希望打包成一个双击就能跑的文件。最成熟的方案是PyInstaller。安装很简单pip install pyinstaller打包单文件pyinstaller -F your_script.py-F表示生成单个exe文件好处是分发方便坏处是启动慢因为运行时需要解压。如果不加-F会生成一个目录里面包含exe和依赖的dll、pyd启动更快但分发时要整个目录打包。打包的时候要注意几点不要直接打包命令行输入交互的程序你同事双击exe会看到黑框建议用input()也好、GUI也好先想清楚交互方式。程序里如果引用了图片、配置文件等资源文件默认不会自动被你打包进去。要用--add-data参数pyinstaller -F --add-data assets;assets your_script.pyWindows上用;分隔源和目标Linux/macOS用:。如果程序用了pathlib读取相对路径打包后的exe工作目录通常是exe所在目录不是程序原来的目录。最好在代码里显式获取exe运行时的目录import sys import os from pathlib import Path if getattr(sys, frozen, False): base_dir Path(sys.executable).resolve().parent else: base_dir Path(__file__).resolve().parentsys.frozen是PyInstaller运行时的标志用它判断是在源码环境还是exe环境然后分别定位基础目录。我踩过这个坑打包出来exe在别的文件夹里双击结果找不到同目录的data.csv最后才反应过来是工作目录切换的问题。PyInstaller还有一个常见坑杀毒软件误报。因为Python的exe启动时会释放临时文件有些杀毒软件会认为是可疑行为。这个没有特别好的根治办法通常可以换个打包参数或者用UPX压缩但误报依然存在。所以如果是正式分发给很多人的工具还是要做好代码签名这个就比较深了。3.4 结构化文件CSV、Excel和65536行问题接下来是很多数据分析场景绕不开的CSV和Excel文件。先说CSV它本质是文本文件所以也可以用open读但用csv模块更安全因为它处理逗号、引号、换行都很规矩import csv with open(data.csv, r, encodingutf-8, newline) as f: reader csv.DictReader(f) for row in reader: print(row[name], row[age])注意newline这个参数特别是在Windows上如果省略写CSV的时候可能会多出空行这是Python文档明确建议的。Excel文件就麻烦一点xls是老格式需要xlrd来读但新版本xlrd只支持xls不支持xlsx读取xlsx推荐用openpyxl或者直接用pandas。而写Excel时如果用到xlwt或者某些旧库会遇到一个经典限制最多只能写65536行。这个数字大家很熟因为老版Excel 2003的.xls格式工作表上限就是65536行。如果你用pandas.to_excel()默认引擎它写的是xlsx没有这个限制行数上限是1048576。但如果你用了ExcelWriter并指定了enginexlwt就会撞上65536。解决办法很简单改用engineopenpyxl或者直接输出为.xlsx。import pandas as pd df.to_excel(output.xlsx, indexFalse, engineopenpyxl)这段代码的意思是把DataFrame写入Excel文件不写索引。engine指定为openpyxl就不会出现行数截断。如果你真的需要写超过一百万行建议导出成CSV那对行数没有任何限制Excel也能打开一部分。处理Excel还要留意单元格样式。如果只是写入数据pandas加openpyxl够了。如果还要设置字体、合并单元格、加筛选那就直接用openpyxl操作from openpyxl import Workbook from openpyxl.styles import Font wb Workbook() ws wb.active ws[A1] 标题 ws[A1].font Font(boldTrue) ws.append([姓名, 分数]) ws.append([张三, 88]) wb.save(report.xlsx)我从实际项目中的体会是大批量数据处理交给pandas精细样式调整交给openpyxl两者可以配合使用但是别混在同一个循环里反复读写Excel性能很差。宁可先把数据整理好最后一次写入。3.5 批量文件处理重命名、移动和备份文件操作最有爽感的场景是批处理。比如你手里有几百个文件名带乱码的图片或者日志按日期命名但格式不统一手工改能改到怀疑人生。用Python写一个脚本只需要几十行。先看一个批量重命名的例子。假设目录./logs/下有这些文件access-20240101.log access-20240102.log access-20240103.log你想把里面的日期改成2024-01-01这种格式。你可以这样写import os import re from pathlib import Path log_dir Path(./logs) for f in log_dir.glob(access-*.log): new_name faccess-{f.stem[-8:]}.log f.rename(f.parent / new_name)注意我用f.stem[-8:]取了文件名中后8位日期然后重新命名。用Path.rename比os.rename更面向对象也更少出现拼路径错的问题。再比如你要把某类文件移动到归档目录import shutil from pathlib import Path source Path(./downloads) target Path(./archive) target.mkdir(exist_okTrue) for f in source.glob(*.pdf): shutil.move(str(f), str(target / f.name))这里的mkdir(exist_okTrue)很关键如果目录不存在会先创建。shutil.move既能移动文件也能重命名。实际批处理时我强烈建议先打印出将要执行的操作确认无误后再真正执行for f in source.glob(*.pdf): dst target / f.name print(f移动 {f} - {dst}) # shutil.move(str(f), str(dst)) # 确认后再取消注释这种“先dry-run再执行”的习惯能避免很多灾难。我自己有一次脚本里正则写错了差一点把几十个文件的版本号全部洗掉还好先跑了一遍print。4. 常见问题与排查技巧实录4.1 文件不存在的报错与检查思路Python报FileNotFoundError最常规的排查三步确认路径是否真的存在最好用Path.exists()先检查一下。确认文件名的大小写在Linux上区分大小写Windows上不区分。确认路径中的目录层级是否完整别少写了一层。还有一个隐藏问题中文文件名或路径。Windows上用中文路径经常遇到编码问题Python 3通常没问题但如果调用一些老库或者子进程可能会乱。最稳妥的办法是尽量用英文路径。如果你被迫处理中文目录可以用pathlib来处理它在底层已经做了Unicode处理。4.2 文件内容读写乱码的排查如果读出来是乱码大概率是编码没对上。用编辑器打开原文件看看右下角显示的编码然后显式用那个编码读。还有一个技巧用requests或open从网络上拿文件时有些网站返回的是utf-8有些是gbk所以用response.encoding先判断一下import requests r requests.get(https://example.com/data.csv) r.encoding utf-8 print(r.text)如果本地文件测试乱码可以试试with open(data.csv, rb) as f: raw f.read() print(raw[:100])打印前100个字节看是不是有\xff\xfeUTF-16 BOM、\xef\xbb\xbfUTF-8 BOM这些标志帮你确认编码。4.3 文件被占用Windows专属的“另一个程序正在使用”Windows用户经常遇到程序运行一半报错“PermissionError: [Errno 13] Permission denied: xxx.xlsx”原因通常是这个文件在Excel里打开着或者上一个脚本进程没有释放句柄。排查办法关掉所有可能打开该文件的程序尤其是Excel、Notepad。检查任务管理器里是不是还有残留的python.exe进程杀掉。如果是自己代码写的检查是不是用了open()但没关闭没有用with。另外如果你在脚本里多次打开同一个Excel文件注意workbook.save之后如果还持有workbook对象文件句柄可能不会立即释放最好workbook.close()。4.4 环境相关命令找不到的问题汇总热词里有一个很典型的“git : 无法将‘git’项识别为 cmdlet、函数、脚本文件或可运行程序的名称”。这类问题跟python环境配置是同一个逻辑你的命令目录不在PATH环境变量里。Python也一样如果你装了Python但是运行python没反应八成也是PATH问题。快速诊断方法在命令行里输入where pythonWindows或which python3Linux/macOS能看到当前使用的是哪个解释器。如果没任何输出说明PATH里没加。这里我顺便说一个比较隐蔽的情况你明明设置了PATH但还是找不到Python那是因为安装时选的是“仅当前用户”PATH变量存在于用户变量里而不是系统变量。这种情况换个用户登录就失效。我一般建议直接装在系统层面或者至少确保用户变量和系统变量都包含Python路径。4.5 防止脚本误操作备份和权限控制最后一条排查技巧其实也是预防技巧。批量改文件之前先做一个备份副本或者把所有文件复制到一个临时目录实验。如果是写文件操作最好先用只读模式打开确认数据再加w模式写。如果你只是要做数据清洗不建议直接覆盖原文件而是生成一个新文件。这样做的好处是万一清洗逻辑写错了原文件还在你能很快回滚。我们写批处理脚本时很推荐这个模式from pathlib import Path src Path(raw_data.txt) dst Path(cleaned_data.txt) with src.open(r, encodingutf-8) as f: lines f.readlines() cleaned_lines [line.strip() for line in lines if line.strip()] with dst.open(w, encodingutf-8) as f: f.writelines(\n.join(cleaned_lines))同样的“不直接修改原文件”原则对于Excel、JSON、配置文件都适用。5. 实战我写的一个文件批处理小工具的全过程5.1 需求描述与设计为了把这些知识点串起来我拿一个近期做过的小工具举例。需求是这样的我有一堆运营导出的CSV文件每个文件里有几千行数据需要把里面某一列的日期从yyyyMMdd转成yyyy-MM-dd然后按月份拆分成多个CSV文件最后把结果打包成一个zip。这个场景非常典型涉及读CSV、按条件筛选、批量生成文件、打包压缩。我用标准库加一点pandas就能搞定。先看目录结构raw_data/ 20240101_export.csv 20240102_export.csv ... output/ (待生成) tool.py5.2 第一步读取并校验文件我先写一个函数遍历raw_data下所有CSV用pandas读取同时打印每个文件的行数和列名确认日期列的格式。import pandas as pd from pathlib import Path import zipfile RAW_DIR Path(raw_data) OUT_DIR Path(output) def load_all_files(): all_frames [] for csv_file in RAW_DIR.glob(*.csv): df pd.read_csv(csv_file, dtype{date: str}) print(f读取 {csv_file.name}: {df.shape}) all_frames.append(df) if not all_frames: raise FileNotFoundError(raw_data目录下没有CSV文件) return pd.concat(all_frames, ignore_indexTrue)这里用dtype{date: str}把日期列强制当作字符串读防止pandas把它自动解析成时间类型后面处理会省去很多格式问题。print一下形状方便确认数据量。5.3 第二步清洗并标准化日期日期列可能是20240101格式也可能已经有2024-01-01为了统一我写一个小函数import datetime as dt def normalize_date(value): try: if - in value: return dt.datetime.strptime(value, %Y-%m-%d).strftime(%Y-%m-%d) return dt.datetime.strptime(value, %Y%m%d).strftime(%Y-%m-%d) except ValueError: # 无法解析就保留原值并打日志 print(f无法解析日期: {value}) return value然后在DataFrame里应用df[date] df[date].apply(normalize_date)这一步注意strptime后面跟着strftime其实等效于重新格式化。如果你最后只需要字符串类型这没问题。如果需要年月日分列就用dt.year、dt.month。5.4 第三步按月分组并导出csv把标准化后的数据按yyyy-MM取前7位生成月份列再groupby拆分最后写入output目录。df[month] df[date].str[:7] OUT_DIR.mkdir(exist_okTrue) for month, group_df in df.groupby(month): month_file OUT_DIR / fdata_{month}.csv group_df.drop(columns[month]).to_csv(month_file, indexFalse, encodingutf-8-sig) print(f已生成 {month_file.name}: {len(group_df)} 行)这里有个细节写CSV时我用了encodingutf-8-sig而不是utf-8。为什么因为在Windows上Excel打开一个不带BOM的UTF-8文件会乱码。utf-8-sig就是带BOM的UTF-8微软的Excel能正确识别。如果是给普通程序读用utf-8更通用。这个细节我在实际交付时被业务同事反馈过改完之后就没再出问题。5.5 第四步打包成zip最终交付给运营时他们不想一个一个下载CSV所以我把output目录打包成zip。zip_path Path(delivery.zip) with zipfile.ZipFile(zip_path, w, zipfile.ZIP_DEFLATED) as zf: for f in OUT_DIR.glob(*.csv): zf.write(f, arcnamef.name)arcname参数可以控制zip里的文件名这里只保留文件名不带目录避免解压后多一层文件夹。ZIP_DEFLATED表示压缩CSV文件一般能压到原大小的30%左右。5.6 整体脚本与运行效果把上面几段组合进一个main()函数并在__main__里调用def main(): df load_all_files() df[date] df[date].apply(normalize_date) df[month] df[date].str[:7] OUT_DIR.mkdir(exist_okTrue) for month, group_df in df.groupby(month): group_df.drop(columns[month]).to_csv(OUT_DIR / fdata_{month}.csv, indexFalse, encodingutf-8-sig) print(f已生成 {month}.csv) with zipfile.ZipFile(delivery.zip, w, zipfile.ZIP_DEFLATED) as zf: for f in OUT_DIR.glob(*.csv): zf.write(f, arcnamef.name) print(全部完成) if __name__ __main__: main()运行输出类似读取 20240101_export.csv: (5120, 8) 读取 20240102_export.csv: (3890, 8) 已生成 data_2024-01.csv 已生成 data_2024-02.csv 全部完成整个脚本四五十行但覆盖了文件遍历、批量读取、转化、导出和压缩。用到的一切都是pandas、pathlib、zipfile。6. 给新手的几个文件操作小技巧6.1 用tempfile处理临时文件写脚本的时候有时候需要生成中间文件用完就扔。如果自己随便建一个tmp.txt最后还得记得删很麻烦。标准库的tempfile可以帮你自动处理import tempfile from pathlib import Path with tempfile.TemporaryDirectory() as tmpdir: tmp Path(tmpdir) / temp.csv tmp.write_text(hello, encodingutf-8) # 处理完之后目录自动清理这个模式在做数据处理流水线时特别方便不用操心垃圾文件残留。6.2 用fileinput快速修改多个文件如果你想在多个文件里做原地替换不想手动先读后写可以用fileinput模块import fileinput for line in fileinput.input(*.txt, inplaceTrue, backup.bak, encodingutf-8): print(line.replace(old, new), end)这个脚本会让*.txt文件里的“old”全部替换成“new”同时备份为.bak。虽然我其实很少推荐原地改文件但如果你明确要做批量修改fileinput确实是最短方案。6.3 用pathlib统一处理目录时的小坑Path.glob默认不会递归到子目录想递归要用rglob。比如找出所有子目录下的.py文件for f in Path(.).rglob(*.py): print(f)另外Path(.)是当前目录但Path(~)并不代表用户目录要用Path.home()。写脚本的时候顺手导入Path能少掉很多路径字符串拼接的麻烦。6.4 检查文件是否为空判断一个文件是否为空别用os.path.getsize()硬记用Path.stat().st_sizefrom pathlib import Path p Path(data.csv) if p.stat().st_size 0: # 非空 pass如果你做的是流式处理还可以先判断文件头几行是否为空再走逻辑避免往下执行一堆无意义操作。7. 常见问题速查表问题现象主要原因解决建议FileNotFoundError路径错误或文件不存在用Path.exists()检查确认大小写UnicodeDecodeError编码与文件实际编码不符显示指定encodingutf-8必要时检测编码Excel导出只有65536行用了老式xls引擎改用openpyxl引擎或导出xlsx文件被占用无法保存文件被其他程序打开或句柄未释放关掉其他程序检查是否漏了with命令行找不到pythonPATH环境变量未配置添加Python目录和Scripts目录到PATHPowerShell禁止运行脚本执行策略限制Set-ExecutionPolicy RemoteSignedPyInstaller打包后找不到文件工作目录变化通过sys.frozen判断exe基础目录csv中文乱码Excel打开UTF-8文件无BOM用utf-8-sig编码导出这张表我建议直接存下来遇到同类问题先对着排查比盲目去翻StackOverflow快得多。从我个人的经验来说Python文件操作的核心不难难的是你总觉得它简单然后忽略边界情况。路径、编码、句柄、环境这四件事处理好了绝大多数脚本都能稳稳跑起来。以后你再写点什么爬虫、数据处理、自动化工具先把地基打好真的能少吃很多苦。