Python读取CSV报错FileNotFoundError:检查当前工作目录与相对路径
详解Python使用open或pandas.read_csv读取CSV文件时报错FileNotFoundError的原因,说明当前工作目录(CWD)与相对路径的解析关系,提供逐步排查方法与pathlib稳定路径写法。
使用 Python 的 open() 或 pandas.read_csv() 读取 CSV 文件时,如果文件确实存在于磁盘上,却依然抛出 FileNotFoundError: [Errno 2] No such file or directory,最常见的原因是相对路径的解析基准与预期不一致。Python 解析相对路径时,始终以进程启动时的“当前工作目录(Current Working Directory,简称 CWD)”为起点,而不是以当前执行的 .py 脚本所在目录为起点。只要先用 os.getcwd() 和 pathlib.Path.resolve() 查明 Python 实际寻找的完整路径,再通过调整运行目录或用 Path(__file__).resolve().parent 锚定脚本目录,就能彻底解决该报错。
一、为什么文件存在还会报 FileNotFoundError
假设本地有一个数据分析项目,根目录为 /Users/dev/project(Windows 下类似 D:\project),目录结构中包含脚本目录 src 和数据目录 data:
project/
├── data/
│ └── sales.csv
└── src/
└── read_data.py
编写 src/read_data.py 时,开发者看到 read_data.py 在 src/ 下,而 sales.csv 在上一级的 data/ 下,于是自然地写出如下相对路径代码:
# 文件位置:project/src/read_data.py
import csv
csv_path = "../data/sales.csv"
with open(csv_path, mode="r", encoding="utf-8") as f:
reader = csv.reader(f)
header = next(reader)
print("表头字段:", header)
如果打开终端,停留在项目根目录 /Users/dev/project 执行运行命令:
python src/read_data.py
示例输出(报错信息):
Traceback (most recent call last):
File "/Users/dev/project/src/read_data.py", line 6, in <module>
with open(csv_path, mode="r", encoding="utf-8") as f:
FileNotFoundError: [Errno 2] No such file or directory: '../data/sales.csv'
报错的直接原因是:终端处于 /Users/dev/project 目录时,Python 进程的当前工作目录就是 /Users/dev/project。此时解析 ../data/sales.csv,会从 /Users/dev/project 向上退一级到 /Users/dev,去寻找并不存在的 /Users/dev/data/sales.csv。只有当终端先进入 /Users/dev/project/src 目录再执行 python read_data.py 时,../data/sales.csv 才会指向正确的 /Users/dev/project/data/sales.csv。
二、按步骤检查当前工作目录与实际解析路径
遇到 FileNotFoundError 时,不要凭肉眼猜测路径层级,而是直接在读取 CSV 之前插入诊断代码,让 Python 输出它当前所在的目录以及将相对路径转换后的完整绝对路径。
在 project/src/read_data.py 中加入如下排查代码,并在 /Users/dev/project 目录下运行:
# 文件位置:project/src/read_data.py
import os
from pathlib import Path
raw_path = "../data/sales.csv"
target = Path(raw_path).resolve()
print("1. 当前工作目录 (os.getcwd):", os.getcwd())
print("2. 脚本所在目录 (__file__):", Path(__file__).resolve().parent)
print("3. 相对路径解析后的绝对路径:", target)
print("4. 目标文件是否存在:", target.exists())
示例输出:
1. 当前工作目录 (os.getcwd): /Users/dev/project
2. 脚本所在目录 (__file__): /Users/dev/project/src
3. 相对路径解析后的绝对路径: /Users/dev/data/sales.csv
4. 目标文件是否存在: False
从输出的第 1 行和第 3 行可以清晰看到:因为当前工作目录是 /Users/dev/project,所以 ../data/sales.csv 被解析成了 /Users/dev/data/sales.csv,与实际存放位置相差了一级目录。
三、两种路径写法对比:基于当前工作目录 vs 基于脚本所在目录
修复路径问题时,通常有两种做法:一种是保持相对路径不变,将其改为相对于当前工作目录(CWD)的写法(例如改为 data/sales.csv);另一种是利用 __file__ 变量,以脚本文件自身所在位置为基准计算路径。在同一个项目结构下,这两种方式在不同启动场景中的表现有显著差异:
| 对比维度 | 方式 A:直接写相对路径(基于 CWD) | 方式 B:使用 Path(__file__).resolve().parent(基于脚本位置) |
|---|---|---|
| 代码示例 | Path("data/sales.csv") |
Path(__file__).resolve().parent.parent / "data" / "sales.csv" |
| 解析起点 | 终端或 IDE 启动 Python 进程时所在的目录(动态变化) | read_data.py 文件在磁盘上的真实父目录(固定不变) |
在 project/ 下运行 python src/read_data.py |
解析为 project/data/sales.csv(成功) |
解析为 project/data/sales.csv(成功) |
在 project/src/ 下运行 python read_data.py |
解析为 project/src/data/sales.csv(报 FileNotFoundError) |
解析为 project/data/sales.csv(成功) |
| 通过定时任务(cron / 任务计划程序)在其他目录调用 | 依赖任务配置的工作目录,未配置则直接报错 | 不受调用位置影响,始终指向项目内的 data/sales.csv |
| 适用场景 | 命令行工具读取用户在当前终端目录下指定的文件;或 Jupyter Notebook 交互分析 | 项目内部固定结构的数据文件、配置文件读取(.py 脚本首选) |
这一差异之所以重要,是因为同一个 Python 脚本往往会在不同位置被调用:有时在 IDE 中点击右上角运行按钮(默认工作目录通常是项目根目录),有时在终端进入 src 子目录调试,有时被其他脚本或定时任务调用。如果 CSV 文件与脚本同属一个项目且相对层级固定,采用方式 B 绑定脚本目录可以避免因启动位置变化而再次报错。
四、稳定的修复方法与完整示例
1. 在 .py 脚本中使用 pathlib 锚定脚本目录(推荐)
Python 3.4 及以上版本内置了 pathlib 标准库,可以跨平台处理 Windows 的反斜杠 \ 与 macOS/Linux 的正斜杠 /。继续以 project/src/read_data.py 读取 project/data/sales.csv 为例,假设 sales.csv 的内容如下:
order_id,product,amount
1001,Keyboard,299.00
1002,Mouse,129.00
修改 project/src/read_data.py,通过 Path(__file__).resolve().parent 获取脚本所在的 src 目录,再通过 .parent 回到 project 根目录拼接 data/sales.csv:
# 文件位置:project/src/read_data.py
# 适用环境:Python 3.6+(若使用 pandas 需已安装 pandas)
import csv
from pathlib import Path
# 1. 获取当前脚本 read_data.py 所在的绝对目录 (即 project/src)
script_dir = Path(__file__).resolve().parent
# 2. 向上退一级到 project 根目录,再拼接 data/sales.csv
csv_path = script_dir.parent / "data" / "sales.csv"
print("正在读取文件:", csv_path)
# 使用内置 csv 模块读取
with open(csv_path, mode="r", encoding="utf-8", newline="") as f:
reader = csv.DictReader(f)
for row in reader:
print(row)
# 如果使用 pandas 读取,同样直接传入 csv_path 即可:
# import pandas as pd
# df = pd.read_csv(csv_path)
# print(df)
此时无论在 /Users/dev/project 运行 python src/read_data.py,还是进入 /Users/dev/project/src 运行 python read_data.py,都能得到一致的正常输出:
正在读取文件: /Users/dev/project/data/sales.csv
{'order_id': '1001', 'product': 'Keyboard', 'amount': '299.00'}
{'order_id': '1002', 'product': 'Mouse', 'amount': '129.00'}
__file__ 变量仅在实际保存为 .py 文件并执行时存在。如果你在 Jupyter Notebook(.ipynb 文件)或 Python 交互式命令行(REPL)中直接运行 Path(__file__),会报错 NameError: name '__file__' is not defined。
2. 在 Jupyter Notebook 中检查与定位 CSV 路径
在 Jupyter Notebook 中编写代码时,默认当前工作目录通常是启动该 .ipynb 文件所在的目录(但也可能被 IDE 配置为项目根目录)。在读取 CSV 前,先在一个单元格中运行以下代码确认当前目录及周围文件:
# 执行位置:Jupyter Notebook 单元格
from pathlib import Path
import pandas as pd
cwd = Path.cwd()
print("Notebook 当前工作目录:", cwd)
# 假设 Notebook 位于 project/notebooks/analysis.ipynb
# 若当前工作目录是 project/notebooks,则向上一级寻找 data/sales.csv
# 若当前工作目录已经是 project 根目录,则直接使用 cwd / "data" / "sales.csv"
csv_path = (cwd.parent / "data" / "sales.csv") if cwd.name == "notebooks" else (cwd / "data" / "sales.csv")
print("解析后的 CSV 路径:", csv_path.resolve())
df = pd.read_csv(csv_path)
print(df.head())
3. 在 VS Code 或 PyCharm 中核对工作目录设置
如果你希望保留相对于项目根目录的写法(如直接写 pd.read_csv("data/sales.csv")),则需要确保编辑器运行脚本时的工作目录固定在项目根目录:
- VS Code:默认情况下,点击右上角“Run Python File”按钮时,工作目录是当前打开的工作区根目录(Workspace Folder)。如果你开启了
python.terminal.executeInFileDir选项,VS Code 会自动将工作目录切换到当前.py文件所在的子目录。可在设置(Settings)中搜索executeInFileDir查看或调整该勾选状态。 - PyCharm:点击顶部运行配置下拉框,选择 Edit Configurations...,查看 Working directory 一栏。PyCharm 默认将其设为当前脚本所在的子目录(如
/Users/dev/project/src),如需统一以项目根目录为基准,可将其手动改为项目根目录/Users/dev/project,确认后点击 Apply 保存。
五、其他导致 FileNotFoundError 的常见细节问题
如果已经核对了当前工作目录,但 Path(csv_path).exists() 仍然返回 False,请重点检查以下三个细节:
1. Windows 路径中的反斜杠被当作转义字符
在 Windows 系统中直接复制文件资源管理器的地址栏路径时,路径分隔符是反斜杠 \。如果直接写成普通字符串 "D:\project\temp\new_sales.csv",其中的 \t 会被 Python 解释为制表符(Tab),\n 会被解释为换行符,导致路径变形而报 FileNotFoundError。
# 错误写法:\t 和 \n 会被转义
bad_path = "D:\project\temp\new_sales.csv"
# 正确写法 1:在字符串前加 r 声明原始字符串(Raw String)
good_path_1 = r"D:\project\temp\new_sales.csv"
# 正确写法 2:统一使用正斜杠 /(Windows 下的 Python 完全支持)
good_path_2 = "D:/project/temp/new_sales.csv"
2. 文件资源管理器隐藏了已知扩展名(实际为 .csv.txt)
在 Windows 或 macOS 中新建文本文件并手动重命名为 sales.csv 时,如果系统开启了“隐藏已知文件类型的扩展名”,文件的真实名称往往是 sales.csv.txt。可以在 Python 中列出目标文件夹下的所有真实文件名进行核实:
from pathlib import Path
data_dir = Path(__file__).resolve().parent.parent / "data"
print("data 目录下的真实文件名列表:", [p.name for p in data_dir.iterdir()])
示例输出:
data 目录下的真实文件名列表: ['sales.csv.txt']
如果输出中显示为 'sales.csv.txt',将文件重命名去掉多余的 .txt 后缀即可。
3. 路径中包含首尾空格或大小写不一致
导出 CSV 文件时,有时文件名末尾会意外多出一个空格(如 "sales .csv"),或者在 Linux 容器环境下运行时文件名大小写不匹配(Linux 文件系统严格区分 Sales.csv 与 sales.csv,而 Windows 和默认 macOS 不区分)。通过上面列出目录文件名的代码,可以直接复制列表中的精确字符串,避免肉眼忽略空格或大小写差异。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













