Python文件路徑處理

2021-12-28 生信編程自修室

收錄於話題 #python編程基礎 5個

文件路徑操作是一個非常基礎但重要的問題,優雅的路徑操作不僅可以讓代碼可讀性更高;還可以讓用戶避免很多不必要的麻煩。python中路徑操作包括三類方法:1. 字符串拼接、2. os.path、3. python 3.4中新增的面向對象的路徑操作庫 pathlib。

本文內容較長且繁瑣,對於初學者建議三個部分循序漸進學習。

字符串拼接

字符串拼接是最原始、最不優雅的路徑操作方式,也是很多初學者偏愛的方式,強烈推薦使用以下兩種更優雅的路徑操作方式。這裡,簡要列舉一下常見的拼接方式(更多字符串操作方法請見Python字符串處理[1])。

In [2]: directory = '/home/jeffery0207'
In [3]: filename = 'a.txt'

In [4]: directory + '/' + filename
Out[4]: '/home/jeffery0207/a.txt'

In [5]: '/'.join([directory, filename])
Out[5]: '/home/jeffery0207/a.txt'

In [6]: f'{directory}/{filename}'  # python3.6之後新增
Out[6]: '/home/jeffery0207/a.txt'

In [7]: '{0}/{1}'.format(directory, filename)
Out[7]: '/home/jeffery0207/a.txt'

In [8]: '%s/%s' % (directory, filename)
Out[8]: '/home/jeffery0207/a.txt'

os.path

os.path模塊是個人比較常用的一個模塊,因為Unix系統和windows系統上的路徑表徵有一定的差別,加載該模塊時python會自動根據所使用的的系統加載不同版本的os.path (posixpath對應於Unix-style的路徑處理;ntpath對應於Windows-style的路徑處理)。該模塊實現了一些實用的路徑處理函數,主要包括:

__all__ = ['expanduser', 'expandvars',
           'abspath', 'realpath', 'relpath',
           'split', 'splitdrive', 'splitext', 'basename', 'dirname', 'join',
           'getatime', 'getctime', 'getmtime', 'getsize',
           'isabs', 'isdir', 'isfile', 'islink', 'ismount', 'exists', 'lexists',
           'samefile', 'sameopenfile', 'samestat',
           'normcase', 'normpath',
           'commonpath', 'commonprefix']

expanduser() 和 expandvars()函數

python默認不會識別shell變量及家目錄符~,可以通過這兩個函數實現擴展

  In [1]: expandvars('$HOME/workspace')
  Out[1]: '/home/liunianping/workspace'
  
  In [2]: expanduser('~/workspace')
  Out[2]: '/home/liunianping/workspace'

split(), splitdrive(), splitext(), basename(), dirname() 和 join()

該模塊中最好用的一組函數,常用於路徑拼接

  In [3]: split(path)
  Out[3]: ('/Users/jeffery0207', 'a.b.c.txt')
  
  In [4]: splitext(path)
  Out[4]: ('/Users/jeffery0207/a.b.c', '.txt')
  
  In [5]: splitdrive(path)
  Out[5]: ('', '/Users/jeffery0207/a.b.c.txt')
    
  In [6]: basename(path)
  Out[6]: 'a.b.c.txt'
  
  In [7]: dirname(path)
  Out[7]: '/Users/jeffery0207'
    
  In [8]: join(dirname(path), 'text.txt')
  Out[8]: '/Users/jeffery0207/text.txt'

isabs(), isdir(), isfile(), islink(), ismount(), exists(), lexists()

該模塊中用於路徑處理判斷的一組函數

  In [9]: isabs(expanduser('~/.zshrc')),isabs('./.zshrc')  # 判斷給定路徑是否是絕對路徑
  Out[9]: (True, False)
    
  In [11]: isdir('./.local') # 判斷給定路徑是否是文件夾類型
  Out[11]: True
    
  In [12]: isfile('./.local')  # 判斷給定路徑是否是普通文件
  Out[12]: False
    
  In [13]: islink('./miniconda3/bin/python')  # 判斷給定路徑是否是符號連結文件
  Out[13]: True
  
  In [14]: ismount('/media/disk01')  # 判斷給定路徑是否是掛載點
  Out[14]: True
  
  In [15]: exists('./miniconda3/bin/python'), lexists('./miniconda3/bin/python')  # 當判斷一個連結文件是否存在時,如果所連結那個文件不存在時,前者返回False,後者返回True
  Out[15]: (True, True)

getatime(), getctime(), getmtime()和getsize()

依次指:返回上次訪問該path的時間;返回該path的系統ctime,在unix系統上對應於該path上次元數據更改的時間,在windows上對應文件的創建時間;返回該path上一次修改的時間;返回該path的文件大小

  In [16]: path = './.zshrc'
  In [17]: getatime(path), getctime(path), getmtime(path), getsize(path)
  Out[17]: (1634019401.9940903, 1633942149.2245831, 1633942149.2205787, 4506)

  In [18]: normcase(path)  # 在windows系統上將所有字符小寫,並將所有正斜槓轉換為反斜槓;其他系統返回不變的path
  Out[18]: './.zshrc'
  
  In [19]: join('./a/', './b.txt')
  Out[19]: './a/./b.txt'
  
  In [20]: normpath(join('./a/', './b.txt'))  # 摺疊多餘的分隔符,在這種情況下非常有用
  Out[20]: 'a/b.txt'
  

commonpath(), commonprefix()

  In [27]: commonpath(['./a/b/c.1.txt', './a/b/c.2.txt'])
  Out[27]: 'a/b'
  
  In [28]: commonprefix(['./a/b/c.1.txt', './a/b/c.2.txt'])
  Out[28]: './a/b/c.'

samefile(path1, path2),sameopenfile(fp1, fp2) , samestat(stat1, stat2)

我們可以創建如下兩個文件作為演示:

  >>> mkdir test && cd test
  >>> touch a.txt
  >>> ln -s a.txt a.link.txt
  >>> ll
  # lrwxr-xr-x  1 jeffery0207  staff     5B Oct 12 21:47 a.link.txt -> a.txt
  # -rw-r--r--  1 jeffery0207  staff     0B Oct 12 21:47 a.txt

首先我們需要了解一下另外三個函數,os.fstat(fd), os.lstat(path, *, dir_fd=None), os.stat(path, *, dir_fd=None, follow_symlinks=True):

os.stat(path, *, dir_fd=None, follow_symlinks=True)

該函數返回一個stat_result對象(stat_result詳細屬性請見os.stat_result[2]),表徵一個文件(file)或則文件描述符(file descriptor)的狀態,等效於系統調用stat:

    $ stat a.txt  # mac系統的顯示
    16777223 12934280785 -rw-r--r-- 1 jeffery staff 0 0 "Oct 12 21:47:10 2021" "Oct 12 21:47:10 2021" "Oct 12 21:47:10 2021" "Oct 12 21:47:10 2021" 4096 0 0 a.txt
    
    $ stat a.txt  # linux 系統的顯示
      File: 『a.txt』
      Size: 4          Blocks: 8          IO Block: 4096   regular file
    Device: fd0ah/64778d Inode: 69309229738  Links: 1
    Access: (0644/-rw-r--r--)  Uid: ( 1042/jeffery)   Gid: (  110/  staff)
    Access: 2021-12-02 22:01:41.000000000 +0800 # 最後訪問時間
    Modify: 2021-12-02 22:01:40.000000000 +0800  # 最後修改時間
    Change: 2021-12-03 10:10:28.992397846 +0800  # 最後狀態改變時間
    Birth: # 創建時間

該函數默認會跟蹤符號連結文件,要獲得符號連結文件本身的狀態,需要設置參數`follow_symlinks=False`或則使用`os.lstat`

os.lstat(path, *, dir_fd=None)

該函數返回一個stat_result對象,表徵一個文件(file)的狀態。從python3.3起,os.lstat(path, *, dir_fd=None)函數等同於os.stat(path, dir_fd=dir_fd, follow_symlinks=False函數。

os.fstat(fd)

該函數返回一個stat_result對象,表徵一個文件描述符(file descriptor)的狀態。從 Python 3.3 開始,這等效於 os.stat(fd)

samestat(stat1, stat2)檢測兩個stat_result對象是否指向同一個文件:

  In [30]: a_link_stat = stat('./test/a.link.txt')
  In [31]: a_stat = stat('./test/a.txt')
  In [32]: a_link_lstat = lstat('./test/a.link.txt')
  In [33]: samestat(a_link_stat, a_stat), samestat(a_link_lstat, a_stat)
  Out[33]: (True, False)

而samefile(path1, path2)函數即底層調用os.stat(path, *, dir_fd=None, follow_symlinks=True)檢測兩個文件是否指向同一個文件:

  In [34]: samefile('./test/a.link.txt', './test/a.txt')
  Out[34]: True

sameopenfile(fp1, fp2)則檢測兩個文件描述符是否是指同一個文件,是則返回True,反之返回False。其先通過依次調用fstat()函數和samestat()函數判斷個文件描述符是否是指同一個文件。

  In [34]: import os
  In [35]: fd_a = os.open('./test/a.txt', os.O_RDWR)
  In [36]: fd_a_link = os.open('./test/a.link.txt', os.O_RDWR)
  In [37]: sameopenfile(fd_a, fd_a_link)
  Out[37]: True

abspath(path), relpath(path, start=os.curdir)), realpath(path, *, strict=False)

分別對應絕對路徑 (等效於normpath(join(os.getcwd(), path)))、相對路徑(返回path相對於start文件夾的路徑)、真實路徑(該函數用於解析連結文件的真實路徑,當strict=False時,如果path不存在或遇到符號連結循環則會拋出OSError錯誤)。

  In [39]: abspath(path)
  Out[39]: '/Users/jeffery/.zshrc'
    
  In [40]: relpath(abspath(path), start='./')  # start 參數默認值為os.curdir,即當前文件夾
  Out[40]: '.zshrc'
  
  In [41]: realpath('./miniconda3/bin/python')  # 一般,我們的python其實是一個連結文件,對應一個具體的python版本文件
  Out[41]: '/Users/jeffery/miniconda3/bin/python3.9'

pathlib

pathlib是python3.4中新增的面向對象的文件系統路徑操作模塊,為不同文件系統提供較為統一的文件系統路徑操作方法。該模塊主要定義了以下類(class),其中pure path包括PurePath, PurePosixPath, PureWindowsPath; concrete paths 包括Path,PosixPath以及WindowsPath,其繼承關係如下圖所示,PurePath 為其他所有類的父類;Path類繼承於PurePath,同時作為PosixPath,WindowsPath的子類。

 from python documentation

因此,pure paths和concrete paths主要區別在於Path類的方法,pure paths類主要封裝了對路徑本身的操作,而concrete paths還包括對文件系統地操作,如新建、刪除等。這種繼承關係可以很好的分離路徑操作和文件系統的操作,更多請參考之前的推文python面向對象編程

PurePath方法和屬性-路徑操作

In [1]: from pathlib import PurePath, PurePosixPath, PureWindowsPath
In [2]: p = PurePath('/Users/jeffery/.zshrc')
In [3]: p1 = PurePath('/Users/jeffery/a.b.c.txt')
In [4]: p2 = PureWindowsPath('c:/foo/bar/setup.py')

  In [5]: p.drive, p2.drive  # Unix-style的路徑沒有盤的概念
  Out[5]: ('', 'c:')

  In [6]: p.root, p2.root
  Out[6]: ('/', '\\')

  In [7]: p1.anchor, p2.anchor  # anchor 就是指 drive和root兩者
  Out[7]: ('/', 'c:\\')

parents

該屬性是路徑的所有父目錄,返回一個Iterable對象[3]

  In [8]: pars = p.parents
  
  In [9]: from collections import Iterable
  In [11]: isinstance(pars, Iterable)
  Out[11]: True
  
  In [12]: for par in pars:
      ...:     print(par)
      ...: 
  /Users/jeffery
  /Users
  /

parent

該屬性指路徑的父目錄,p.parent則是/Users/jeffery

name

該屬性指路徑最後一層級的名稱,但不包括drive和root,可方便的用於取文件名或文件夾名

  In [13]: p1.name
  Out[13]: 'a.b.c.txt'
  In [14]: PurePath('/').name
  Out[14]: ''

  In [15]: p.suffix  # 取文件擴展名
  Out[15]: ''
  In [16]: p1.suffix
  Out[16]: '.txt'

  In [17]: p1.suffixes  # 取多層級的文件擴展名,其本質就是根據.進行字符串分割
  Out[17]: ['.b', '.c', '.txt']

stem

相當於先取path.name,然後再去除文件擴展名(path.suffix)

  In [18]: p1.stem
  Out[18]: 'a.b.c'

  In [19]:  p = PureWindowsPath('c:\\windows')
  In [20]: str(p)
  Out[20]: 'c:\\windows'
  In [21]:  p.as_posix()
  Out[21]: 'c:/windows'

as_uri()

將路徑轉換成文件url的形式,參數path必須是絕對路徑,否則拋出ValueError

  In [22]: p1.as_uri()
  Out[22]: 'file:///Users/jeffery/a.b.c.txt'

is_absolute()

判斷一個路徑是否是絕對路徑的形式

is_relative_to(*other)

該方法判斷路徑是否和另一個路徑是相對關係,換句話說就是other是否是路徑的父目錄或則相同目錄(當路徑本身是目錄時)

  In [26]: p1.is_relative_to('/Users/jeffery')
  Out[26]: True
  In [27]: p1.is_relative_to('/Users')
  Out[27]: True
  In [28]: p1.is_relative_to('/Users/jeffery/c.txt')
  Out[28]: False

is_reserved()

判斷一個目錄是否是作業系統中一些特殊的文件設備名,如nul, com1, com2等,該命令只針對windows-like path, Posix-like path總是返回False.

  In [35]: PureWindowsPath('nul').is_reserved()
  Out[35]: True

joinpath(*other)

路徑名連接,等效於操作符/

  In [32]: PurePosixPath('/etc').joinpath('init.d', 'apache2')
  Out[32]: PurePosixPath('/etc/init.d/apache2')
  
  In [33]: PurePosixPath('/etc') / 'init.d' / 'apache2'
  Out[33]: PurePosixPath('/etc/init.d/apache2')

  In [34]: PurePath('/a/b/c.py').match('b/*.py')
  Out[34]: True
  
  In [35]: PurePath('/a/b/c.py').match('a/*.py')  
  Out[35]: False
    
  In [36]: PurePath('/a/b/c.py').match('/a/*.py') # 特別注意這一點,和shell的匹配模式不一樣
  Out[36]: False

relative_to(*other)¶

以other路徑為起點,計算路徑的相對路徑,如果無法計算則拋出ValueError

  In [39]: p.relative_to('/')
  Out[39]: PurePosixPath('etc/passwd')
  
  In [40]: p.relative_to('/etc/passwd')
  Out[40]: PurePosixPath('.')

以上代碼等效於os.path.relpath:

  In [42]: from os.path import relpath
  In [43]:  relpath('/etc/passwd', start='/')
  Out[43]: 'etc/passwd'
  In [44]: relpath('/etc/passwd', start='/etc/passwd')
  Out[44]: '.'

with_name(name)

該方法將路徑中原本的name替換為新的name,如果路勁中本身不存在name,則拋出ValueError錯誤

  In [45]: p1.with_name('new.txt')
  Out[45]: PurePosixPath('/Users/jeffery/new.txt')
    
  ## 用os.path實現
  In [46]: from os.path import join, split
  In [47]: join(split('/Users/jeffery/a.b.c.txt')[0], 'new.txt')
  Out[47]: '/Users/jeffery/new.txt'

with_stem(stem)

該方法將路徑中原本的stem替換為新的stem,如果路勁中本身不存在name,則拋出ValueError錯誤

  In [51]:  p1.with_stem('new')
  Out[51]: PurePosixPath('/Users/jeffery/new.txt')
    
  ## 用os.path實現
  In [52]: from os.path import join, split, splitext
  In [53]: join(split("/Users/jeffery/a.b.c.txt")[0],'new' + splitext("/Users/jeffery/a.b.c.txt")[1])
  Out[53]: '/Users/jeffery/new.txt'

with_suffix(suffix)

該方法將路徑中原本的suffix替換為新的suffix,如果suffix="",則原本的suffix直接移除

  In [54]: p1.with_suffix('.py')
  Out[54]: PurePosixPath('/Users/jeffery/a.b.c.py')

ConcretePath方法和屬性-文件系統操作

為了探索ConcretePath方法和屬性,我們首先在家目錄下建一個新的文件tmp, 並在tmp下新建文件a.txt。

In [1]: from pathlib import PosixPath, Path, WindowsPath

In [2]: p1 = PosixPath('./tmp')

In [3]: p2 = Path('./tmp/a.txt')

cwd()

該方法返回當前所在文件夾,等同於os.getcwd(),因此可以知道該方法應該是一個classmethod[4],其調用與具體的對象無關

  In [4]: Path.cwd(), PosixPath.home()
  Out[4]: (PosixPath('/Users/jeffery'), PosixPath('/Users/jeffery'))

home()

該方法也是一個classmethod[5],返回用戶的家目錄路徑,等同於os.path.expanduser()

stat(*, follow_symlinks=True)

該方法類似於前面介紹的os.stat(),返回一個os.stat_result對象(stat_result詳細屬性請見os.stat_result[6])。所以,類似的,follow_symlinks=False時,可以統計連結文件的信息, 等效於os.lstat。

  In [5]: stat_result  = p2.stat()
  
  In [6]: stat_result
  Out[6]: os.stat_result(st_mode=33188, st_ino=12936268394, st_dev=16777223, st_nlink=1, st_uid=501, st_gid=20, st_size=15, st_atime=1637994757, st_mtime=1637994751, st_ctime=1637994751)

chmod(mode, *, follow_symlinks=True)

修改路徑權限,類似於os.chmod()或則shell 命令chmod。這裡我們使用

  In [10]: p2.chmod(0o655)  # (python3)這裡使用8進位,更為簡明
  In [11]: ll tmp/
  -rw-r-xr-x  1 jeffery  staff  15 Nov 27 14:32 a.txt

exists()

判斷路徑是否存在,值得注意的是,當路徑是連結文件時,該方法會進一步追蹤查看對應的符號連結是否存在

expanduser()

同上面的os.path.expanduser(),如果家目錄無法確定的話,則拋出RuntimeError

glob(pattern)

根據pattern搜索路徑中匹配的文件,pattern的匹配規則同fnmatch[7],主要利用以下unix shell-like通配符進行簡單的文件匹配任務(如更複雜的任務,請參考python re 正則表達式[8]):

PatternMeaning*matches everything?matches any single character[seq]matches any character in seq[!seq]matches any character not in seq

glob(pattern)方法返回一個generator[9]類型,可以通過迭代的方法取出所有匹配的文件:

  In [13]: g = p1.glob('*.txt')
  
  In [14]: for _g in g:
      ...:     print(_g)
      ...: 
  tmp/a.txt
  
  In [15]: type(g)
  Out[15]: generator

python標準庫glob[10]中glob.glob()及glob.iglob()函數也是利用unix shell-like 通配符進行文件匹配。前者返回數據類型為list,後者返回類型為iterator。

  In [16]: from glob import glob, iglob, escape
  In [17]: glob('./tmp/*.txt')
  Out[17]: ['./tmp/a.txt']
  In [18]: iglob('./tmp/*.txt')
  Out[18]: <generator object _iglob at 0x110e8e510>

需要注意的是,glob.glob()默認情況下是不會匹配隱藏文件的;而Path.glob()會匹配隱藏文件:

  In [19]: for _g in p1.glob('*.txt'):
      ...:     print(_g)
      ...: 
  tmp/.a.txt
  tmp/a.txt
  
  In [20]: glob('./tmp/*.txt')
  Out[20]: ['./tmp/a.txt']

另外,當文件匹配的路徑中含有通配符時,我們可以藉助glob.escape()函數進行快速注釋,比如我們新建文件mkdir -p "./tmp/[ABC]/b.txt",則我們需要利用以下方式進行匹配:

  In [21]: from os.path import join
  
  In [22]: glob(join(escape('./tmp/[ABC]'), '*.txt'))
  Out[22]: ['./tmp/[ABC]/b.txt']

is_dir()

判斷路徑是否是文件夾,返回bool值;需要注意的是如果文件不存在或符號連結文件有問題時,也返回False(以下類似函數也適用該規則)。

is_file()

判斷路徑是否是文件,返回bool值

is_mount()

判斷路徑是否是掛載點,返回bool值

is_symlink()

判斷路徑是否是符號連結文件,返回bool值

is_socket()

判斷路徑是否是socket連接,返回bool值

is_fifo()

判斷路徑是否是FIFO管道文件,返回bool值

is_block_device()

判斷路徑是否是塊設備文件,返回bool值

is_char_device()

判斷路徑是否是字符設備文件,返回bool值

iterdir()

遍歷文件夾,並返迴路徑中所有的文件

  In [23]: for _dir in p1.iterdir():
      ...:     print(_dir)
      ...: 
  tmp/.a.txt
  tmp/a.txt
  tmp/[ABC]

lchmod(mode)

不同於Path.chmod(mode)默認效果,該函數修改文件權限時,如果路徑是連結文件,不會同時修改對應符號連結文件的權限

lstat()

不同於Path.stat(),當路徑是連結文件時,返回連結文件的統計信息而非它的目標文件

mkdir(mode=511, parents=False, exist_ok=False)

創建文件夾

open(mode='r', buffering=- 1, encoding=None, errors=None, newline=None)

打開文件,類似於python內置函數open()

read_bytes()

讀取bytes,相當於打開並讀取兩步操作

  In [24]: p2.read_bytes()
  Out[24]: b'This is a bird\n'
  
  In [25]: open('./tmp/a.txt', 'rb').read()
  Out[25]: b'This is a bird\n'

read_text(encoding=None, errors=None)

類似於read_bytes(),但是以text形式讀取文件

readlink()

該方法返迴路徑的目標連結文件,如果路徑不是連結文件,則拋出OSError

  In [56]: ll ./tmp/ # 命令行新建文件a.link.txt ln -s a.txt a.link.txt
  total 8
  drwxr-xr-x  3 jeffery  staff  96 Nov 30 11:27 [ABC]/
  lrwxr-xr-x  1 jeffery  staff   5 Nov 30 15:03 a.link.txt@ -> a.txt
  -rw-r-xr-x  1 jeffery  staff  15 Nov 30 11:16 a.txt*
  
  In [54]: Path('./tmp/a.link.txt').readlink()
  Out[54]: PosixPath('a.txt')

rename(target)

重命名文件,返回值為更新後指向target的對象,如果target所對應的路徑已經存在,在權限允許下,會直接覆蓋。target參數既可以是字符串類型也可以是path對象。

target參數可以是相對路徑,也可以是絕對路徑;當相對路徑時,相對的是Path.cwd()而不是path 對象。

  In [55]: p = Path('foo')
  
  In [56]: p.open('w').write('some text')
  Out[56]: 9
  
  In [57]: target = Path('bar')
  
  In [58]: p.rename(target)
  Out[58]: PosixPath('bar')
  
  In [59]: target.open().read()
  Out[59]: 'some text'

replace(target)

同上,重命名文件,返回值為更新後指向target的對象。如果target對象已經存在,則會無條件的覆蓋

resolve(strict=False)

返迴路徑的絕對路徑,如果路徑是連結文件,則返迴路徑對應符號連結文件的絕對路徑

  In [60]: p2.resolve()
  Out[60]: PosixPath('/Users/jeffery/tmp/a.txt')
  
  In [61]: Path('./tmp/a.link.txt').resolve()
  Out[61]: PosixPath('/Users/jeffery/tmp/a.txt')

strict=False時,如果文件不存在,它也會強行解析出其路徑;strict=True時,如果文件不存在,則拋出FileNotFoundError錯誤

In [62]: Path('./tmp/text.txt.zip').resolve()
Out[62]: PosixPath('/Users/jeffery/tmp/text.txt.zip')

In [63]: Path('./tmp/text.txt.zip').resolve(strict=True)
FileNotFoundError: [Errno 2] No such file or directory: '/Users/jeffery/tmp/text.txt.zip'

rglob(pattern)

該方法等同於Path.glob("**/{pattern}")

samefile(other_path)

返回bool值,判斷路徑與other_path是否指向同一個文件。如果任一文件不存在或權限問題而無法訪問,則拋出OSError。該方法類似於上面探討到的os.path.samefile() 和 os.path.samestat()。

symlink_to(target, target_is_directory=False)

該方法連結路逕到target路徑,當target是文件夾時,在windows下需要設置target_is_directory=True; 在unix環境下,該參數是忽略的。

  In [64]: Path('./tmp/link.txt').symlink_to('./tmp/a.txt')
    
  -rw-r-xr-x  1 jeffery  staff    15B Nov 30 11:16 a.txt
  lrwxr-xr-x  1 jeffery  staff    11B Dec  2 21:43 link.txt -> ./tmp/a.txt

hardlink_to(target)

類似於symlink_to(),該方法創建硬連結

link_to(target)

與hardlink_to()相反,該方法中target作為硬連結文件連結到對象對應路徑

  In [65]: p2.link_to('./tmp/hard.link.txt')
    
  -rw-r-xr-x  2 jeffery  staff    15B Nov 30 11:16 a.txt
  -rw-r-xr-x  2 jeffery  staff    15B Nov 30 11:16 hard.link.txt

touch(mode=438, exist_ok=True)

創建文件,當exist_ok=True且對應文件已存在時,會拋出FileExistsError。

unlink(missing_ok=False)

刪除文件或移除符號連結文件,當missing_ok=False且路徑不存在時,拋出FileNotFoundError

write_bytes(data)

read_bytes反操作,寫入bytes,相當於打開並寫入bytes兩步操作

  In [66]: p2.write_bytes(b'xxxx') # 覆蓋式寫入
  Out[66]: 4

write_text(data, encoding=None, errors=None, newline=None)

類似於write_bytes,但是以text形式寫入文件

對照表

python documentation官方網站也給出了pathlib庫與os及os.path模塊實現相同功能函數的對照表如下,如果你已經熟悉後者,可以依據該表格快速的進行」遷移學習「。

os and os.pathpathlibos.path.abspath()Path.resolve() os.chmod()Path.chmod()os.mkdir()Path.mkdir()os.makedirs()Path.mkdir()os.rename()Path.rename()os.replace()Path.replace()os.rmdir()Path.rmdir()os.remove(), os.unlink()Path.unlink()os.getcwd()Path.cwd()os.path.exists()Path.exists()os.path.expanduser()Path.expanduser() and Path.home()os.listdir()Path.iterdir()os.path.isdir()Path.is_dir()os.path.isfile()Path.is_file()os.path.islink()Path.is_symlink()os.link()Path.hardlink_to()os.symlink()Path.symlink_to()os.readlink()Path.readlink()os.path.relpath()Path.relative_to() 2os.stat()Path.stat(), Path.owner(), Path.group()os.path.isabs()PurePath.is_absolute()os.path.join()PurePath.joinpath()os.path.basename()PurePath.nameos.path.dirname()PurePath.parentos.path.samefile()Path.samefile()os.path.splitext()PurePath.suffix權限碼轉換

前面有個例子,涉及到python中權限編碼的問題,這裡進一步展開開探討一下:

In [1]: from pathlib import Path

In [2]: p = Path('./tmp/a.txt')

In [3]: sr =p.stat()

In [4]: sr
Out[4]: os.stat_result(st_mode=33188, st_ino=12936268394, st_dev=16777223, st_nlink=1, st_uid=501, st_gid=20, st_size=4, st_atime=1638497905, st_mtime=1638453700, st_ctime=1638497208)

在命令行中,我們同樣可以查看到文件的權限詳情:

$ ll a.txt 
-rw-r--r--  1 jeffery  staff     4B Dec  2 22:01 a.txt

那麼問題來了,st_mode=33188和-rw-r--r--;以及st_mode=33188和644具有怎樣的對應關係呢?

首先,我們可以利用標準庫stat模塊中的``函數進行轉換:

In [5]: import stat
In [6]: stat.filemode(stat_result.st_mode)  # 33188 代表的權限就是644
Out[6]: '-rw-r--r--'

但是st_mode=33188包含的信息其實不僅僅包括文件讀寫權限的信息:

In [5]: oct(sr.st_mode)  # 轉換成八進位,我們注意到八進位包含644,其實八進位最後三位對應的就是文件的讀寫權限編碼
Out[5]: '0o100644'

st_mode=33188是一個十進位數,轉換成八進位後,我們注意到八進位包含644,其實八進位最後三位對應的正是文件的讀寫權限編碼,stat.filemode()正是利用該特性進行權限字符串轉換的,規則如下:

S_IRWXU 00700 mask for file owner permissions
S_IRUSR 00400 owner has read permission
S_IWUSR 00200 owner has write permission
S_IXUSR 00100 owner has execute permission
S_IRWXG 00070 mask for group permissions
S_IRGRP 00040 group has read permission
S_IWGRP 00020 group has write permission
S_IXGRP 00010 group has execute permission
S_IRWXO 00007 mask for permissions for others (not in group)
S_IROTH 00004 others have read permission
S_IWOTH 00002 others have write permission
S_IXOTH 00001 others have execute permission

而'0o100644'前面的部分0100000代表的則是文件類型---regular file,具體規則如下:

S_IFMT 0170000 bitmask for the file type bitfields
S_IFSOCK 0140000 socket
S_IFLNK 0120000 symbolic link
S_IFREG 0100000 regular file
S_IFBLK 0060000 block device
S_IFDIR 0040000 directory
S_IFCHR 0020000 character device
S_IFIFO 0010000 FIFO
S_ISUID 0004000 set UID bit
S_ISGID 0002000 set-group-ID bit
S_ISVTX 0001000 sticky bit

#寫在篇後

這篇博客內容本身非常簡單,但是所提到的兩個模塊包含了大量的API,需要我們在日常的使用中靈活應用,選擇最高效、優雅的方式。其中,pathlib模塊面向對象編程的路徑處理方式,能夠非常從容的應對較大的項目開發需求,高度統一路徑操作、文件系統操作,比如單細胞轉錄組分析分析軟體scanpy[11]中便是採用了pathlib庫進行路徑處理工作,是項目開發中不可多得的好例子。

Reference[1]

Python字符串處理:https://blog.csdn.net/jeffery0207/article/details/79477629

[2]

os.stat_result:https://docs.python.org/3/library/os.html#os.stat_result

[3]

Iterable對象:https://blog.csdn.net/jeffery0207/article/details/88112825

[4]

classmethod:https://blog.csdn.net/jeffery0207/article/details/89042654

[5]

classmethod:https://blog.csdn.net/jeffery0207/article/details/89042654

[6]

os.stat_result:https://docs.python.org/3/library/os.html#os.stat_result

[7]

fnmatch:https://docs.python.org/3/library/fnmatch.html#module-fnmatch

[8]

python re 正則表達式:https://blog.csdn.net/jeffery0207/article/details/81806714

[9]

generator:https://blog.csdn.net/jeffery0207/article/details/88112825

[10]

glob:https://docs.python.org/3/library/glob.html

[11]

scanpy:https://github.com/theislab/scanpy

[12]

python pathlib:https://docs.python.org/3/library/pathlib.html

[13]

python stat:https://docs.python.org/3/library/stat.html

[14]

get unix permission mask:https://stackoverflow.com/questions/5337070/how-can-i-get-the-unix-permission-mask-from-a-file

相關焦點

  • [Python]文件與異常的錯誤處理
    [Python]文件與異常的錯誤處理1.文件的處理過程# 文件基本操作:打開文件,處理文件,關閉文件the_file = open("sketch.txt", 'r')line_content = the_file.readline()  # 不指定readline的參數,讀取一整行print(line_content)
  • glob - 被忽略的python超強文件批量處理模塊
    ./*.py"):    print(fname)樣例二:當前路徑文件下以 .python開頭並且有一個字符的所有py文件.for fname in glob.glob("./python?.py"):    print(fname)樣例三:當前路徑文件下以 .python開頭並且有一個數字的所有py文件.for fname in glob.glob(".
  • Python處理CSV文件
    在數據處理過程中,可能會需要採用篩選、提取、重新生成等方法來對數據進行處理,下面以csv文件為例,介紹使用Python處理文本文件的方法。開始之前在面對文件讀寫類的問題時,open函數是一定繞不開的,下面先介紹幾種open函數使用的技巧。
  • python中的文件路徑操作,學習os.path和pathlib
    ==============OUTPUT================__pycache__file_sum.md5 input1 input2 pivot.xlsx pyshell.py step1.res tes test.file test.ipynb test.sh test1.sh可以看到目前的目錄下有這些個文件,下面就開始具體的操作
  • 實例15:用Python批量轉換doc文件為docx文件
    python-docx模塊雖然強大,但卻不能處理後綴為".doc"的word文件。如果強制讀取doc文件,將會報如下錯誤。
  • 技巧|深度對比Python中4大文件/文件夾處理庫
    一、開篇os庫應該是使用頻率最高的一個文件處理庫,但是不得不說Python中還有幾個其它的文件處理庫,像shutil庫、glob庫、pathlib庫,它們可以說是相互補充,有著自己好用的方法。路徑拼接Path對象,進行路徑拼接,直接使用一個/斜槓即可。p = Path('C:/Users/Administrator/Desktop/python三劍客')p1 = p/'pathlib庫'p1
  • python-docx無法處理 「doc格式」 文件,於是我這樣做......
    問題引入目前的日常工作中,見的最多的還是對Excel文件和 Word文件的處理。
  • python使用with as處理文件的讀寫
    文件處理的兩種情況:1、忘記關閉文件。2、文件讀寫異常,未做處理。在python中使用with語句,可以自動調用close()方法,同時也解決了異常問題。with open('test.txt','w') as f:f.write('Hello, python!')
  • 數據處理技巧 | glob - 被忽略的超強文件批量處理模塊
    ./*.py"):    print(fname)樣例二:當前路徑文件下以 .python開頭並且有一個字符的所有py文件.for fname in glob.glob("./python?.py"):    print(fname)樣例三:當前路徑文件下以 .python開頭並且有一個數字的所有py文件.for fname in glob.glob(".
  • 使用Python圖像處理庫Pillow處理圖像文件
    本案例使用圖像處理庫Pillow中的模塊、對象來處理圖像:實現讀取圖像、獲取圖像信息、調整圖像大小、旋轉圖像、平滑圖像、剪切圖像等基本圖像處理任務。CS2.1 安裝PillowPillow是Python中的圖像處理庫(PIL:Python Image Library),提供了了廣泛的文件格式支持,強大的圖像處理能力,主要包括圖像儲存、圖像顯示、格式轉換以及基本的圖像處理操作等。
  • CSV文件在Python中的幾種處理方式
    ,拆分後再次寫入另一張CSV文件中(如果要將數據寫入xls*格式的文件中,請參考前期公眾號文章),步驟如下:Step 1:導入必要模塊,獲取輸入輸出文件路徑。 三、使用csv模塊讀寫CSV文件 csv模塊是Python內置的一個模塊,它考慮了csv文件中的各種複雜情況,平時處理文件基本上使用此模塊,下面來看看csv模塊處理csv文件的方式
  • 使用Python下載文件的簡單示例
    @2x.png"wget.download(url, 'c:/users/LikeGeeks/downloads/pythonLogo.png')在這段代碼中,URL以及路徑(圖像將在其中存儲)被傳遞給wget模塊的下載方法。
  • Python對CSV文件的處理
    CSV的文件中。在Python中,讀取csv文件使用到的標準庫是csv,直接導入就可以了,要讀取的CSV文件內容為:見讀取CSV文件裡面內容的源碼:#!/usr/bin/env python #-*-coding:utf-8-*-#author:wuyaimport  csvdef readCsv(): with open('csvTest.csv','r') as f: rander=csv.reader(f) #對數據循環獲取
  • python實戰演練2:python可執行文件運行不成功怎麼辦
    而目前常用的製作python的可執行文件工具為pyinstaller(對python版本有一定要求,python2.x的版本需要是2.4-2.7,python3.x的版本要在python3.4以上。下面是打包方式使用下載安裝的方式安裝的Pyinstaller打包方式將需要打包的文件放在解壓得到的Pyinstaller文件夾中,打開cmd窗口,把路徑切換到當前路徑打開命令提示行,輸入以下內容(最後的是文件名:比如你當前要打包的是comp_file.py這個文件):python pyinstaller.py -F comp_file.py使用pip方式安裝的
  • 使用Python處理NetCDF格式文件
    本文不對NetCDF數據格式進行過多的介紹,主要講一下如何處理NetCDF格式文件,並且如何對最終的數據進行可視化分析。目前有非常多的軟體可以處理NetCDF格式文件,並且相當一部分提供了可視化功能。複雜的數據處理工作和二維可視化可以使用matlab,python或NCL,三維可視化可以使用VisAD,Vis5d,IDV等。處理nc文件的工具很多,此次僅利用python來講一下如何處理nc文件。
  • python的路徑解析(獲取相對路徑並且自動導入)
    python路徑解析假設我們現在有一個目錄文件夾如下, api目錄下有a文件,然後有b目錄,b目錄下有
  • Python讀入文件報錯的處理方法
    文章導讀:本文主要 解決python在讀入文件時的報錯:ParserError:Error tokenizing data.C error:Expected x fields in line nnn,saw y。文章分為兩部分,第一部分解釋報錯的意思,定位原因;第二部分給出解決方法。
  • 4招小技巧,帶你迅速提升Python文件處理的逼格
    這是菜鳥學python的第31篇原創文章閱讀本文大概需要6分鐘文件這塊知識已經講了好幾篇,第一篇是2個小例子入門第二篇是文件語法和場景的一個總覽第三篇是一個綜合小例子實戰一下,可以融合前面所學的知識這一篇算是收尾吧,回憶我以前寫過的python程序,我覺得python文件處理有這麼幾個模塊非常重要,但是一般沒有人告訴你
  • Python讀取ini配置文件
    python看過我之前文章的同學可能知道,最近一直在做百度語音合成的功能,進一步的延伸功能,此次是批量生成文章的語音文件目前是使用python來實現的,將代碼打包成了exe文件,方便客戶進行測試。下面說一下配置文件,本次直說ini格式的配置文件。創建ini文件在目錄中創建.ini文件,我們以config.ini文件名為例。進行創建。在創建好的文件內編寫配置信息。
  • Python一鍵自動化整理文件,任意路徑下文件,都給你整理的明明白白!
    但是手動整理太費時間了,於是我想到了python。2.升級版基於這個,我想是否可以對其他不同的路徑進行整理呢,於是又優化了一下。"class DeskTopOrg(object):    def __init__(self):        self.filetype = FileType()    def Organization(self):        filepath = input("請輸入需要整理的文件夾路徑: ")        paths =