文件批量读取与路径管理:用os和glob玩转文件夹
做光谱分析,最头疼的是什么?
我个人觉得,不是算法多复杂,也不是数据多难处理。而是——你得先搞定那一堆乱七八糟的文件。
你想想看,一次实验下来,少说几十个光谱文件。命名方式五花八门,有的带日期,有的带样品编号,有的干脆就叫"新建文件夹"。更别提不同操作系统下,路径分隔符还不一样。Windows用反斜杠,Linux用正斜杠,Mac有时候还搞特殊。
嗯,今天我们就来聊聊怎么优雅地搞定这件事。
为什么非得用os和glob?
说白了,手动一个一个文件去处理,那是刚入行才干的事。我刚开始做光谱分析那会儿,也傻乎乎地手动改文件名、手动复制路径。直到有一次,我连续处理了200多个文件,眼睛都快瞎了,还漏掉了3个。
从那以后,我就养成了一个习惯:能用脚本解决的,绝不动手。
Python的os模块和glob模块,就是干这个的。前者负责跟操作系统打交道,后者专门帮你找文件。
os模块:跟操作系统对话的基础
os模块是Python内置的,不需要额外安装。它提供了很多跟操作系统交互的函数。
获取当前路径
import os
# 获取当前工作目录
current_dir = os.getcwd()
print(f"当前目录:{current_dir}")
# 列出目录下所有文件和文件夹
all_items = os.listdir(current_dir)
print(f"目录内容:{all_items}")
我在项目中遇到过一个问题:有时候脚本跑着跑着,路径就乱了。后来我习惯在脚本开头先os.chdir()到固定目录,这样就不会迷路。
路径拼接——别再用加号了
很多人喜欢用字符串加号拼接路径,比如path = folder + "/" + filename。这种做法,说实话,挺危险的。
为什么?因为不同操作系统的分隔符不一样。你写死在代码里的正斜杠,到了Windows上可能就报错。
# 正确做法:用 os.path.join()
folder = "data/spectra"
filename = "sample_001.csv"
full_path = os.path.join(folder, filename)
print(full_path)
# Windows 输出:data\spectra\sample_001.csv
# Linux/Mac 输出:data/spectra/sample_001.csv
看到了吗?os.path.join会自动根据当前操作系统选择正确的分隔符。这个细节,我曾经吃过亏。有一次把写好的脚本发给同事,他在Mac上跑,路径全乱了。从那以后,我再也不用加号拼路径了。
判断文件还是文件夹
import os
path = "some_file.txt"
if os.path.isfile(path):
print("这是一个文件")
elif os.path.isdir(path):
print("这是一个文件夹")
else:
print("路径不存在")
os.path.exists() 先判断路径是否存在,可以避免很多莫名其妙的报错。
glob模块:文件搜索利器
glob模块,说白了就是帮你做文件匹配的。它支持通配符,用起来非常方便。
基础用法
import glob
# 查找当前目录下所有 .csv 文件
csv_files = glob.glob("*.csv")
print(csv_files)
# 递归查找子目录
all_csv = glob.glob("**/*.csv", recursive=True)
print(all_csv)
这里有个坑,我提醒一下:recursive=True这个参数,在Python 3.5以上才支持。如果你还在用老版本,记得升级。
结合正则表达式过滤文件名
有时候,光靠通配符不够用。比如你想找出所有以"sample_"开头、后面跟三位数字的.csv文件。这时候就得请出正则表达式了。
import glob
import re
# 先用 glob 拿到所有 .csv 文件
all_csv = glob.glob("*.csv")
# 再用正则过滤
pattern = r"^sample_\d{3}\.csv$"
filtered = [f for f in all_csv if re.match(pattern, f)]
print(filtered)
我个人习惯把正则表达式写成一个函数,方便复用:
def filter_files(file_list, pattern):
"""根据正则表达式过滤文件名"""
return [f for f in file_list if re.match(pattern, f)]
# 使用示例
csv_files = glob.glob("*.csv")
target_files = filter_files(csv_files, r"^sample_\d{3}\.csv$")
构建文件路径列表——实战案例
好了,理论说完了。我们来看一个完整的实战案例。
假设你的光谱数据存放在这样的目录结构中:
data/
├── 2024-01/
│ ├── sample_001.csv
│ ├── sample_002.csv
│ └── sample_003.csv
├── 2024-02/
│ ├── sample_004.csv
│ ├── sample_005.csv
│ └── sample_006.csv
└── 2024-03/
├── sample_007.csv
├── sample_008.csv
└── sample_009.csv
你想批量读取所有以"sample_"开头的.csv文件。怎么做?
import os
import glob
import re
def build_file_list(root_dir, pattern):
"""
构建文件路径列表
参数:
root_dir: 根目录
pattern: 文件名正则表达式
返回:
匹配的文件路径列表
"""
# 递归查找所有 .csv 文件
all_csv = glob.glob(os.path.join(root_dir, "**", "*.csv"), recursive=True)
# 过滤文件名
filtered = []
for file_path in all_csv:
filename = os.path.basename(file_path)
if re.match(pattern, filename):
filtered.append(file_path)
return filtered
# 使用示例
root = "data"
pattern = r"^sample_\d{3}\.csv$"
file_list = build_file_list(root, pattern)
print(f"找到 {len(file_list)} 个文件:")
for f in file_list:
print(f)
这段代码,我用了好几年,基本没出过问题。你拿去稍微改改,就能用在你的项目里。
处理不同操作系统的路径差异
这个问题,说实话,挺烦人的。但也不是没办法解决。
统一使用正斜杠
Python的pathlib模块(Python 3.4+)提供了一个更现代的方式:
from pathlib import Path
# 创建路径对象
path = Path("data/spectra/sample_001.csv")
print(path) # 自动使用当前系统的分隔符
# 路径拼接
folder = Path("data/spectra")
full_path = folder / "sample_001.csv"
print(full_path)
# 获取文件名、目录名
print(path.name) # sample_001.csv
print(path.stem) # sample_001
print(path.suffix) # .csv
print(path.parent) # data/spectra
我个人更推荐用pathlib,因为它更直观,代码也更简洁。不过如果你在维护老项目,可能还是得用os.path。
跨平台路径转换
import os
# Windows 路径转 Linux 路径
win_path = "data\\spectra\\sample_001.csv"
linux_path = win_path.replace("\\", "/")
print(linux_path) # data/spectra/sample_001.csv
# 或者用 os.path.normpath
normalized = os.path.normpath(win_path)
print(normalized) # 根据当前系统转换
/,Python 在 Windows 下也能识别。这样你的代码拿到任何系统上都能跑。
知识体系总览
下面这张图,是我自己整理的。它把文件批量读取和路径管理的核心逻辑串起来了:
避坑指南
最后,分享几个我踩过的坑:
- 路径中有空格: 我曾经有个文件夹叫"my data",结果脚本死活找不到文件。后来发现是路径没加引号。用
os.path.join可以避免这个问题。 - 隐藏文件:
glob.glob("*")默认不匹配以点开头的隐藏文件。如果你需要,得用glob.glob(".*")单独处理。 - 递归深度: 用
**递归查找时,如果目录层级太深,可能会很慢。建议先print一下看看结果对不对,再继续处理。 - 编码问题: 中文文件名在Windows下有时会乱码。我习惯在脚本开头加上
# -*- coding: utf-8 -*-,然后用os.listdir时指定编码。
好了,文件批量读取和路径管理这块,就聊到这儿。你把这些技巧用熟了,处理光谱数据的时候,能省下不少时间。