一篇文章搞懂Python的文件路徑操作
前言
如果你要在代碼里讀取一個(gè)文件,那么你首先要知道這個(gè)文件的路徑。如果只有一個(gè)文件,那么很簡(jiǎn)單,直接復(fù)制這個(gè)文件所在的文件夾路徑及其文件名即可。而在很多情況下,我們會(huì)處理大量的文件,這些文件一般都會(huì)按一定的規(guī)則存放在一個(gè)或幾個(gè)文件夾里。本文便是簡(jiǎn)單講一下怎么應(yīng)對(duì)這種情況,將以Python為例,但其中的理念是通用的。
1 什么是文件路徑
文件路徑簡(jiǎn)單地說(shuō)就是文件的存放位置,它包含具體的盤(pán)符號(hào),也就是位于電腦上哪個(gè)磁盤(pán)分區(qū)、哪個(gè)文件夾(目錄)和最終這個(gè)文件的名稱(chēng)+文件類(lèi)型擴(kuò)展名。
文件的路徑表示用戶(hù)在磁盤(pán)上尋找文件時(shí),所歷經(jīng)的文件夾線路;路徑分為絕對(duì)路徑和相對(duì)路徑;絕對(duì)路徑是從根文件夾開(kāi)始的路徑;相對(duì)路徑是從當(dāng)前文件夾開(kāi)始的路徑。
1.1 絕對(duì)路徑
不同操作系統(tǒng)下絕對(duì)路徑的表現(xiàn)形式是不一樣的,以Windows系統(tǒng)為例,一個(gè)文件的路徑可能是這樣的:
D:\files\data\ndvi.tif
其中:
D:\:表示根文件夾,是文件所在的盤(pán)符,即D盤(pán)。D:\files\data:表示文件所在的文件夾的路徑,即D盤(pán)的files文件夾的子文件夾data。ndvi.tif:表示文件名,其中ndvi是基本名,用來(lái)標(biāo)識(shí)這個(gè)文件;tif是擴(kuò)展名,用來(lái)反映文件的類(lèi)型,二者用.分開(kāi)。
Linux和MacOS下的絕對(duì)路徑和Windows系統(tǒng)不同,主要區(qū)別如下:
- 根文件夾不同,Windows的根文件夾是盤(pán)符,如D:\、C:\;而在Linux和MacOS中,根文件夾是/,你可以理解為所有的文件都在一個(gè)盤(pán)下,自然不需要用C、D這樣的字符去區(qū)分了。
- 分隔符不同,在Windows 上,路徑書(shū)寫(xiě)使用倒斜杠\作為文件夾之間的分隔符。但在MacOS和Linux上,使用正斜杠/作為它們的路徑分隔符。
- 大小寫(xiě)區(qū)分不同,文件夾名稱(chēng)和文件名在Windows和MacOS上是不區(qū)分大小寫(xiě)的,但在Linux上是區(qū)分大小寫(xiě)的。
附加卷的路徑:
附加卷,諸如DVD驅(qū)動(dòng)器或USB閃存驅(qū)動(dòng)器,在不同的操作系統(tǒng)上顯示也不同。在Windows上,它們表示為新的、帶字符的根驅(qū)動(dòng)器。諸如
D:\或E:\。在MacOS上,它們表示為新的文件夾,在/Volumes文件夾下。在Linux上,它們表示為新的文件夾,在/mnt文件夾下。
1.2 相對(duì)路徑
相對(duì)路徑是指以當(dāng)前工作目錄為參照基礎(chǔ),鏈接到目標(biāo)文件資源(或文件夾)的路徑。
相對(duì)路徑的表示符號(hào)如下:
- 以
./開(kāi)頭,代表當(dāng)前目錄和文件目錄在同一個(gè)目錄里,./也可以省略不寫(xiě); - 以
../開(kāi)頭:向上走一級(jí),代表目標(biāo)文件在當(dāng)前文件所在的上一級(jí)目錄; - 以
../../開(kāi)頭:向上走兩級(jí),代表父級(jí)的父級(jí)目錄,也就是上上級(jí)目錄,再說(shuō)明白點(diǎn),就是上一級(jí)目錄的上一級(jí)目錄; - 以
/開(kāi)頭,代表根目錄。
相對(duì)路徑使用示例:

2 Python對(duì)路徑的操作
2.1 在Python中怎么表示文件路徑
在Python中,一般使用字符串存儲(chǔ)文件路徑。但需要注意的是,字符反斜杠\在Python中表示轉(zhuǎn)義字符。因此,在表示W(wǎng)indows系統(tǒng)下的文件路徑(Windows系統(tǒng)的分隔符是\)時(shí)需注意以下要點(diǎn):
- 以路徑D:\files\data\ndvi.tif為例;
- 在字符串前加個(gè)字符r,表示該字符串為原始字符串,會(huì)完全忽略所有的轉(zhuǎn)義字符。例如,r"D:\files\data\ndvi.tif";
- 對(duì)轉(zhuǎn)義字符進(jìn)行轉(zhuǎn)義,例如,"D:\\files\\data\\ndvi.tif";
- 將分隔符替換為/,是的,在Windows系統(tǒng)下,將分隔符替換為/Python也能正確識(shí)別。例如,"D:/files/data/ndvi.tif"。
Linux和MacOS下,直接將路徑放到單引號(hào)或者雙引號(hào)里就行。
2.2 創(chuàng)建新文件夾
可以用os.mkdir()函數(shù)創(chuàng)建新文件夾(目錄),使用os.path.isdir()函數(shù)判斷一個(gè)路徑是不是文件夾,如下所示:
import os path = "D:/files/data" if os.path.isdir(path): os.mkdir(path)
需要注意的是,os.mkdir()函數(shù)只能創(chuàng)建單級(jí)目錄。如上面的代碼所示,只有"D:/files"目錄存在,才能在其下創(chuàng)建data目錄。要想創(chuàng)建多級(jí)目錄,則要使用os.makedirs()函數(shù),例如:
import os path = "D:/files/data" if os.path.isdir(path): os.makedirs(path)
os.listdir()方法用于返回指定的文件夾包含的文件或文件夾的名字的列表,只支持在 Unix, Windows 下使用,例如:
>>> import os
>>> os.listdir("D:/files/data")
['ndvi.tif', 'ndvi_2023_01.tif']
2.3 拼接和拆分文件夾
os.path.join()函數(shù)用于路徑拼接文件路徑,可以傳入多個(gè)路徑,它會(huì)根據(jù)操作系統(tǒng)的不同自動(dòng)確定分隔符。
>>> import os
>>> os.path.join(r'D:\files\data', 'ndvi_2023_01.tif')
'D:\\files\\data\\ndvi_2023_01.tif'
>>> os.path.join('D:/files/data', 'ndvi_2023_01.tif')
'D:/files/data\\ndvi_2023_01.tif'
>>> os.path.join('./data', 'ndvi_2023_01.tif')
'./data\\ndvi_2023_01.tif'
>>> os.path.join('files', 'data', 'ndvi_2023_01.tif')
'files\\data\\ndvi_2023_01.tif'os.path.split()函數(shù)可以把一個(gè)路徑拆分為兩部分,后一部分總是最后級(jí)別的目錄或文件名。os.path.splitext()函數(shù)同樣可以把一個(gè)路徑拆分為兩部分,后一部分總是最后的文件擴(kuò)展名。
>>> os.path.split("D:/files/data/ndvi.tif")
('D:/files/data', 'ndvi.tif')
>>> os.path.split("D:/files/data")
('D:/files', 'data')
>>> os.path.splitext("D:/files/data/ndvi.tif")
('D:/files/data/ndvi', '.tif')
>>> os.path.splitext("D:/files/data")
('D:/files/data', '')此外,os.path.basename()函數(shù)用于獲取路徑最后的文件名或文件夾名,os.path.dirname()函數(shù)用于去掉最后的文件名或文件夾名,返回余下的文件夾路徑。
>>> os.path.basename("D:/files/data/ndvi.tif")
'ndvi.tif'
>>> os.path.basename("D:/files/data")
'data'
>>> os.path.dirname("D:/files/data/ndvi.tif")
'D:/files/data'
>>> os.path.dirname("D:/files/data")
'D:/files'2.4 處理絕對(duì)路徑和相對(duì)路徑
os.path模塊提供了一些函數(shù),返回一個(gè)相對(duì)路徑的絕對(duì)路徑,以及檢查給定的路徑是否為絕對(duì)路徑。
- os.getcwd():獲取當(dāng)前工作目錄;
- os.path.abspath(path):返回參數(shù)的絕對(duì)路徑的字符串,以當(dāng)前工作目錄為基準(zhǔn)。這是將相對(duì)路徑轉(zhuǎn)換為絕對(duì)路徑的簡(jiǎn)便方法;
- os.path.isabs(path):如果參數(shù)是一個(gè)絕對(duì)路徑,就返回True,如果參數(shù)是一個(gè)相對(duì)路徑,就返回 False;
- os.path.relpath(path, start):將返回從start路徑到path的相對(duì)路徑的字符串。如果沒(méi)有提供start,就使用當(dāng)前工作目錄作為開(kāi)始路徑。
代碼示例:
>>> os.getcwd()
'C:\\Python34'
>>> os.path.abspath('.')
'C:\\Python34'
>>> os.path.abspath('.\\Scripts')
'C:\\Python34\\Scripts'
>>> os.path.isabs('.')
False
>>> os.path.isabs(os.path.abspath('.'))
True
>>> os.path.relpath('C:\\Windows', 'C:\\')
'Windows'
>>> os.path.relpath('C:\\Windows', 'C:\\spam\\eggs')
'..\\..\\Windows'2.4 使用glob查找文件夾或文件
glob模塊用來(lái)查找文件目錄和文件,并將搜索的到的結(jié)果返回到一個(gè)列表中。
在使用glob模塊之前,需要先了解一下它的三個(gè)通配符,即*、?和[],其具體含義如下:
*:代表0個(gè)或多個(gè)字符;?:代表一個(gè)字符;[]:匹配指定范圍內(nèi)的字符,如[0-9]匹配數(shù)字;[a-c]匹配字母a、b或c,不區(qū)分大小寫(xiě);[12a]匹配字母1、2或a。
下面通過(guò)一個(gè)例子詳細(xì)講一下這三個(gè)通配符怎么使用,假如你有如下所示的目錄結(jié)構(gòu):
+-- D:/
| +-- data1
| | +-- readme.md
| | +-- ndvi.tif
| | +-- buliding.tif
| +-- data2
| | +-- ndvi.tif
| | +-- water.tif
| +-- picture
| | +-- mm.tif
|
假如你要查找data1下的所有以.tif結(jié)尾的文件,你可以這樣寫(xiě):
>>> from glob import glob
>>> glob('D:/data1/*.tif')
['D:/data1/ndvi.tif', 'D:/data1/buliding.tif']假如你要查找data開(kāi)頭的目錄下的所有名為ndvi.tif的文件,你可以這樣寫(xiě):
>>> glob('D:/data*/ndvi.tif')
['D:/data1/ndvi.tif', 'D:/data2/ndvi.tif']
>>> glob('D:/data?/ndvi.tif')
['D:/data1/ndvi.tif', 'D:/data2/ndvi.tif']
>>> glob('D:/data[1-2]/ndvi.tif')
['D:/data1/ndvi.tif', 'D:/data2/ndvi.tif']3 多個(gè)文件的路徑操作示例
在數(shù)據(jù)處理中,很多時(shí)候我們都會(huì)有這樣的要求,處理多個(gè)文件,并且要按照一定的順序。最常見(jiàn)的就是按照時(shí)間順序排列各個(gè)文件的路徑,比如按年、月、日等,下面詳細(xì)介紹幾個(gè)例子。
3.1 年尺度數(shù)據(jù)
假如你有如下目錄結(jié)構(gòu):
+-- D:/
| +-- data
| | +-- 2000.tif
| | +-- 2001.tif
... ... ... ... ...
| | +-- 2019.tif
| | +-- 2020.tif
|
如果你要讀取data目錄下2000-2020年所有文件的路徑(按時(shí)間順序排列),你可以這樣寫(xiě):
import os
from glob import glob
# glob會(huì)自動(dòng)排序這些文件路徑,排序的規(guī)則為文件名
paths = glob('D:/data/*.tif*')
# 或者這樣寫(xiě),可以指定開(kāi)始和結(jié)束年份
paths = []
root_dir = 'D:/data'
start_year, end_year = 2000, 2020
for year in range(start_year, end_year):
path = os.path.join(root_dir, f'{year}.tif')
paths.append(path)
3.2 月尺度數(shù)據(jù)
假如你有如下目錄結(jié)構(gòu):
+-- D:/
| +-- data
| | +-- 200001.tif
| | +-- 200002.tif
... ... ... ... ...
| | +-- 200012.tif
| | +-- 200101.tif
... ... ... ... ...
| | +-- 202012.tif
|
假如你要讀取data目錄下某一年或某幾年所有月的tif數(shù)據(jù),你可以這樣寫(xiě):
import os
from glob import glob
# 讀取2015年所有月的數(shù)據(jù)
year = 2015
paths = glob(f'D:/data/{year}*.tif*')
# 讀取2015-2020年所有月的數(shù)據(jù)
paths = []
root_dir = 'D:/data'
start_year, end_year = 2015, 2020
for year in range(start_year, end_year):
path += os.path.join(root_dir, f'{year}*.tif')假如你要讀取data目錄特定年在某個(gè)季節(jié)的tif數(shù)據(jù),你可以這樣寫(xiě):
import os
from glob import glob
# 假設(shè)一年的冬季為當(dāng)年的1月、2月以及上一年的12月
season_months = {'spring': ['03', '04', '05'], 'summer': ['06', '07', '08'],
'autumn': ['09', '10', '11'], 'winter': ['01', '02']}
paths = []
root_dir = 'D:/data'
season = 'spring'
years = [2014, 2015, 2016]
for year in years:
months = season_months[season]
for month in months:
path = os.path.join(root_dir, 'f{year}{month}.tif')
assert path
paths.append(path)
if season == 'winter':
path = os.path.join(root_dir, 'f{year-1}{12}.tif')
assert path
paths.append(path)3.3 日尺度數(shù)據(jù)
假如你有如下目錄結(jié)構(gòu),如文件名中的1982001表示數(shù)據(jù)拍攝時(shí)間為1982年的第1天。
./ndvi/AVH13C1.A1982001.N07.005.2017161044559.NDVI.tif —— 1982001 —— 1982年第1天
./ndvi/AVH13C1.A1982002.N07.005.2017161050433.NDVI.tif —— 1982002 —— 1982年第2天
./ndvi/AVH13C1.A1982003.N07.005.2017161052408.NDVI.tif —— 1982003 —— 1982年第3天
./ndvi/AVH13C1.A1982004.N07.005.2017161054145.NDVI.tif —— 1982004 —— 1982年第4天
./ndvi/AVH13C1.A1982005.N07.005.2017161055856.NDVI.tif —— 1982005 —— 1982年第5天
./ndvi/AVH13C1.A1982006.N07.005.2017161061328.NDVI.tif —— 1982006 —— 1982年第6天
假如你要讀取某一年年某一月中所有天的數(shù)據(jù)的路徑,你可以這樣寫(xiě):
import os
import calendar
from glob import glob
from datetime import datetime
def get_year_month_paths(root_path, year, month):
'''獲取某年某月所有NDVI文件的路徑'''
paths = []
# 用于glob函數(shù)的通配符字符串,可視作文件名模板
fmt = '*{}{:03}*.tif'
# 計(jì)算該年該月共有多少天
month_days = calendar.monthrange(year, month)[1]
# 計(jì)算該年該月的第一天是一年中的第幾天
month_start_day = (datetime(year, month, 1) - datetime(year, 1, 1)).days + 1
for i in range(month_days):
day_num = month_start_day + i
month_day_path = glob(os.path.join(root_path, fmt.format(year, day_num)))
if not month_day_path:
print('{}年{}月的數(shù)據(jù)缺失,編號(hào)為{}{:03}'.format(year, month, year, day_num))
paths += month_day_path
return paths
3.4 文件的時(shí)間順序隱藏在文件內(nèi)
有些文件你下載時(shí)的默認(rèn)文件名是一堆無(wú)意義的字符,而你知道這些文件里存儲(chǔ)的有時(shí)間信息,所以就沒(méi)有對(duì)這些文件一一命名。當(dāng)你要用時(shí)你就會(huì)發(fā)現(xiàn)很麻煩,要按時(shí)間順序讀取這些文件,靠文件名沒(méi)法做到,而文件太多,一個(gè)個(gè)打開(kāi)看一下時(shí)間再重命名又太慢了。這樣只能在代碼里一個(gè)個(gè)打開(kāi)文件,讀取其時(shí)間,并依據(jù)這些時(shí)間對(duì)文件的路徑進(jìn)行排序。這種情況常見(jiàn)于.nc或.hdf格式的數(shù)據(jù),其內(nèi)部會(huì)存儲(chǔ)時(shí)間信息,而有些網(wǎng)站下載文件時(shí)文件名有沒(méi)有時(shí)間信息。
假如你有如下目錄結(jié)構(gòu):
./RH/ked.nc
./RH/mmm.nc
./RH/dii.nc
./RH/jkd.nc
./RH/zex.nc
./RH/xyz.nc
每個(gè)nc文件下都有一個(gè)名為time的變量,類(lèi)型為數(shù)組,用于記錄時(shí)間。以ERA5數(shù)據(jù)為例,其小時(shí)尺度的數(shù)據(jù)記錄的當(dāng)前時(shí)間距格里尼治時(shí)間1900-01-01-00:00的小時(shí)數(shù),若要將其轉(zhuǎn)為東八區(qū)的字符串格式的時(shí)間,可以這么做:
import datetime
import netCDF4 as nc
ds = nc.Dataset('./RH/ked.nc')
time = ds['time'][...].data
origin_date = datetime.datetime(1900, 1, 1, 0, 0)
start_date = origin_date+datetime.timedelta(hours=int(times[0])+8)
start_date = start_date.strftime('%Y-%m-%d-%H')
而如果我們不需要對(duì)這些nc文件重命名,只是要在代碼里對(duì)這些文件的路徑進(jìn)行排序,只需要這么做:
from glob import glob
nc_paths = glob(os.path.join('./RH', '*.nc'))
times = []
for nc_path in nc_paths:
# 提取各站點(diǎn)的溫度
ds = nc.Dataset(nc_path)
time = ds.variables['time']
times.append(int(time[0].data))
_, nc_paths = (list(t) for t in zip(*sorted(zip(times, nc_paths))))
最后,對(duì)文件路徑進(jìn)行操作的可能性有無(wú)數(shù)種,掌握這些基本知識(shí),學(xué)會(huì)舉一反三,多動(dòng)手嘗試,只有這樣在數(shù)據(jù)處理中才能游刃有余。
總結(jié)
到此這篇關(guān)于Python文件路徑操作的文章就介紹到這了,更多相關(guān)Python文件路徑操作內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python中網(wǎng)絡(luò)請(qǐng)求中Retry策略實(shí)現(xiàn)方式
這篇文章主要介紹了Python中網(wǎng)絡(luò)請(qǐng)求中Retry策略實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-06-06
python實(shí)現(xiàn)微信遠(yuǎn)程控制電腦
這篇文章主要為大家詳細(xì)介紹了python實(shí)現(xiàn)微信遠(yuǎn)程控制電腦的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-02-02
Pytorch 如何實(shí)現(xiàn)LSTM時(shí)間序列預(yù)測(cè)
本文主要基于Pytorch深度學(xué)習(xí)框架,實(shí)現(xiàn)LSTM神經(jīng)網(wǎng)絡(luò)模型,用于時(shí)間序列的預(yù)測(cè)2021-05-05
python操作excel的方法(xlsxwriter包的使用)
這篇文章主要為大家詳細(xì)介紹了python操作excel的方法,xlsxwriter包的使用方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06
深入理解Python虛擬機(jī)中描述器的實(shí)現(xiàn)原理
這篇文章主要給大家介紹一個(gè)我們?cè)谑褂妙?lèi)的時(shí)候經(jīng)常使用但是卻很少在意的黑科技——描述器的實(shí)現(xiàn)原理,文中的示例代碼講解詳細(xì),需要的可以參考一下2023-05-05
python GUI庫(kù)圖形界面開(kāi)發(fā)之PyQt5輸入對(duì)話框QInputDialog詳細(xì)使用方法與實(shí)例
這篇文章主要介紹了python GUI庫(kù)圖形界面開(kāi)發(fā)之PyQt5輸入對(duì)話框QInputDialog詳細(xì)使用方法與實(shí)例,需要的朋友可以參考下2020-02-02
pandas的object對(duì)象轉(zhuǎn)時(shí)間對(duì)象的方法
下面小編就為大家分享一篇pandas的object對(duì)象轉(zhuǎn)時(shí)間對(duì)象的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-04-04
python使用paramiko實(shí)現(xiàn)ssh的功能詳解
這篇文章主要介紹了python使用paramiko實(shí)現(xiàn)ssh的功能詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-03-03

